Railway Operation Simulator  v2.13.0 Beta
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TExitInfo::TExitInfo() //default constructor
63 {
64  ServiceReference = " ";
65  RepeatNumber = 0;
66  TimeToExitSecs = -1;
67 }
68 
69 // ---------------------------------------------------------------------------
70 
71 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
72  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
73  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
74  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
75  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
76  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
77 /*
78  Construct a new train with general default values and input values for position and headcode.
79  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
80  This is because trains are kept in a vector and vectors erase elements during internal operations.
81  Deletion is explicit by using a special function. Increment the static class member NextTrainID
82  after setting this train's ID.
83 */
84 
85 {
86  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
87  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
88  AnsiString(TrainModeIn));
89  // AutoControl = true;//all trains start in auto control
90  UpdateCounter = 0;
91  TimeTimeLocArrived = false;
92  Derailed = false;
93  DerailPending = false;
94  Crashed = false;
95  StoppedAtBuffers = false;
96  StoppedAtSignal = false;
97  StoppedAtLocation = false;
98  StoppedAfterSPAD = false;
99  StoppedWithoutPower = false; // new at v2.4.0
100  StoppedForTrainInFront = false;
101  SignallerStoppingFlag = false;
102  SignallerStopped = false;
103  SignallerRemoved = false;
104  NotInService = false;
105  HoldAtLocationInTTMode = false;
106  AllowedToPassRedSignal = false;
107  CallingOnFlag = false;
108  BeingCalledOn = false;
109  DepartureTimeSet = false;
111  TimetableFinished = false;
112  LastActionDelayFlag = false;
113  OneLengthAccelDecel = false;
114  TrainCrashedInto = -1;
116  Plotted = false;
117  TrainGone = false;
118  SPADFlag = false;
119  FrontCodePtr = new Graphics::TBitmap;
120  FrontCodePtr->PixelFormat = pf8bit;
121  FrontCodePtr->Height = 8;
122  FrontCodePtr->Width = 8;
124  FrontCodePtr->Transparent = false;
125  AValue = sqrt(2 * PowerAtRail / Mass);
127  TerminatedMessageSent = false;
128  JoinedOtherTrainFlag = false;
130  FollowOnServiceRef = ""; //added at v2.12.0
131  TreatPassAsTimeLocDeparture = false; //added at v2.12.0
132  StepForwardFlag = false;
134  for(int x = 0; x < 4; x++)
135  {
136  HeadCodeGrPtr[x] = new Graphics::TBitmap;
137  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
138  HeadCodeGrPtr[x]->Height = 8;
139  HeadCodeGrPtr[x]->Width = 8;
141  HeadCodeGrPtr[x]->Transparent = false;
142  }
143  for(int x = 0; x < 4; x++)
144  {
145  BackgroundPtr[x] = new Graphics::TBitmap;
146  BackgroundPtr[x]->PixelFormat = pf8bit;
147  BackgroundPtr[x]->Height = 8;
148  BackgroundPtr[x]->Width = 8;
150  BackgroundPtr[x]->Transparent = false;
151  }
152  for(int x = 0; x < 4; x++)
153  {
155  // set here to ensure have values
156  }
157  for(int x = 0; x < 4; x++)
158  {
159  PlotElement[x] = -1; // marker for not plotted yet
160  }
161  for(int x = 0; x < 3; x++)
162  {
163  OldZoomOutElement[x] = -1; // marker for not plotted yet
164  }
166  NextTrainID++;
167 
168  // new values added to complete initialisation of all TTrain variables
169 
170  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
171  // TrainDataEntryPtr, initialise in AddTrain
173  FrontElementLength = 0;
174  EntrySpeed = 0;
175  ExitSpeedHalf = 0;
176  ExitSpeedFull = 0;
177  MaxExitSpeed = 0;
178  BrakeRate = 0;
180  FirstHalfMove = true;
181  EntryTime = 0;
182  ExitTimeHalf = 0;
183  ExitTimeFull = 0;
184  ReleaseTime = 0;
185  TRSTime = 0;
186  LastActionTime = 0;
187  Straddle = MidLag;
188  LeadElement = -1;
189  LeadEntryPos = 0;
190  LeadExitPos = 0;
191  MidElement = -1;
192  MidEntryPos = 0;
193  MidExitPos = 0;
194  LagElement = -1;
195  LagEntryPos = 0;
196  LagExitPos = 0;
197  TrainFailed = false; // added at v2.4.0
198  for(int x = 0; x < 4; x++)
199  {
200  HOffset[x] = 0;
201  VOffset[x] = 0;
202  PlotEntryPos[x] = 0;
203  }
204  OpTimeToAct = 60; // default value, new at v2.2.0
205  TimeToExit = -1;
206  ExitPair.first = -1;
207  ExitPair.second = -1;
208  MinsDelayed = 0.0; // new at v2.2.0
209  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
210  FinishJoinLogSent = false;
211  // added at v2.4.0 to prevent repeatdly logging the event
214  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
218  ZeroPowerNoCDTMessage = false;
223  TrainFailurePending = false;
224  SkippedDeparture = false;
225  ActionsSkippedFlag = false;
226  SkipPtrValue = 0;
227  TrainSkippedEvents = 0;
228  DelayedRandMins = 0; //added at v2.13.0
229  NewDelay = 0; //added at v2.13.0
230  CumulativeDelayedRandMinsOneTrain = 0; //added at v2.13.0
231  ActualArrivalTime = TDateTime(0); //added at v2.13.0
232  Utilities->CallLogPop(648);
233 }
234 
235 // ---------------------------------------------------------------------------
236 
237 void TTrain::DeleteTrain(int Caller)
238 /*
239  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
240  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
241  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
242  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
243  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
244  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
245  No need to delete HeadCodePosition as that just points to existing bitmaps
246 */{
247  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
248  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
249  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
250  if(Display->ZoomOutFlag)
251  {
253  }
254  if(FrontCodePtr == 0)
255  {
256  throw Exception("Error in attempting to delete FrontCodePtr");
257  }
258  delete FrontCodePtr;
259  FrontCodePtr = 0;
260  for(int x = 0; x < 4; x++)
261  {
262  if(BackgroundPtr[x] == 0)
263  {
264  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
265  }
266  delete BackgroundPtr[x];
267  BackgroundPtr[x] = 0;
268  }
269  for(int x = 0; x < 4; x++)
270  {
271  if(HeadCodeGrPtr[x] == 0)
272  {
273  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
274  }
275  delete HeadCodeGrPtr[x];
276  HeadCodeGrPtr[x] = 0;
277  }
278  Utilities->CallLogPop(649);
279 }
280 
281 // ---------------------------------------------------------------------------
282 
284 /*
285  Plots the train starting position on screen. Note that the check for starting on straight points &
286  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
287  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
288  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
289  Set the headcode graphics pointers from the headcode text, then check whether starting at a
290  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
291  for the continuation element. Otherwise set Lead and Mid values,
292 
293  and Lead element value unless
294  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
295  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
296  then check if a train on either Mid or Lag and if so give a warning message and return false so
297  that the calling function can delete the train. Plot the Mid element train values then do similarly
298  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
299  the train. Finally set the Plotted flag and return true.
300 */{
301  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
302  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
303 
305  // PlotStartTime = TrainController->TTClockTime;
306  FirstHalfMove = true;
307 
308  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
309  // 'claim' it for this train to prevent any other waiting trains trying to enter
311  {
312  LagElement = -1; // not to be plotted
313  LagExitPos = 0; // not to be plotted
314  LagEntryPos = 0; // not to be plotted
315  MidElement = -1; // not to be plotted
316  MidExitPos = 0; // not to be plotted
317  MidEntryPos = 0; // not to be plotted
319  LeadExitPos = 1; // will be 1 for continuation entry
320  LeadEntryPos = 0;
321 
323  MaxExitSpeed = StartSpeed; // initial value
325  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
326  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
327  if(EntrySpeed > SpeedLimit)
328  {
329  EntrySpeed = SpeedLimit;
330  }
332  {
334  }
336  // LeadElement is the element to be entered
337 
338  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
339  // can achieve ExitSpeedFull at the half braking rate.
341  {
342  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
343  if(TempEntrySpeed < EntrySpeed)
344  {
345  EntrySpeed = TempEntrySpeed;
347  }
348  }
349  Straddle = MidLag; // only for starting on a continuation
351  // no need to stop gap flashing if start on continuation
352  }
353  else // not starting at a continuation
354  {
355  LagElement = -1;
356  LagEntryPos = 0;
357  LagExitPos = 0;
364 
366  MaxExitSpeed = StartSpeed; // initial value
368  bool TempDerail = false; // dummy
369  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
371  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
372  {
373  StoppedWithoutPower = true;
374  }
375  // facing buffers check - ignore starting speed if start facing buffers
376  StoppedAtBuffers = false;
377  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
380  {
381  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
382  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
383  EntrySpeed = 0;
384  ExitSpeedHalf = 0;
385  ExitSpeedFull = 0;
386  MaxExitSpeed = 0;
387  // SetTrainMovementValues not called so set this here
388  BrakeRate = 0;
391  StoppedAtSignal = false;
392  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
393  // signal check is an 'else'
394  if(!StoppedAtLocation)
395  {
396  StoppedAtBuffers = true; // stopped at location takes precedence
397  }
398  }
399 
400  // facing continuation check - don't allow to stop even if no power
402  {
403  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
404  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
408  BrakeRate = 0;
409  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
410  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
411  }
412 
413  // Signal check
414  else if((NextElementPosition > -1) && (NextEntryPos > -1))
415  // condition check added as precaution after SloughIECC error reported by James U
416  {
417  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
418  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
419  {
420  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
421  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
422  EntrySpeed = 0;
423  ExitSpeedHalf = 0;
424  ExitSpeedFull = 0;
425  MaxExitSpeed = 0;
426  BrakeRate = 0;
429  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
430  {
431  StoppedAtSignal = true;
433  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
434  }
436  {
437  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
438  // forwards, but don't change the background colour so still shows as stopped at location
439  StoppedAtSignal = true;
440  }
441  }
442  else
443  {
444  StoppedAtSignal = false;
445  if(NextEntryPos > 1)
446  {
447  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
448  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
449  }
450  else
451  {
452  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
453  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
454  }
455  if(EntrySpeed > SpeedLimit)
456  {
457  EntrySpeed = SpeedLimit;
458  }
460  {
462  }
464  TDateTime TestTime = TrainController->TTClockTime; // test
465  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
466  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
467  // NextElement is the element to be entered
468 
469  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
470  // can achieve ExitSpeedFull at the half braking rate.
472  {
473  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
474  // half braking
475  if(TempEntrySpeed < EntrySpeed)
476  {
477  EntrySpeed = TempEntrySpeed;
478  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
479  }
480  }
481  }
482  }
484  {
485  throw Exception("Error, LeadElement Exit Connection is NotSet");
486  }
487  }
488  if(MidElement > -1) // will be -1 if start on continuation
489  {
490  Straddle = LeadMid;
494  {
495  for(int x = 0; x < 4; x++)
496  {
497  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
498  }
499  }
500  else
501  {
502  for(int x = 0; x < 4; x++)
503  {
505  }
506  }
507  if(TrainMode == Timetable)
508  {
510  }
511  else
512  {
514  }
516  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
517 
520 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
521  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
522  {
523  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
524  Utilities->CallLogPop(651);
525  return false;
526  }
527 */
532  PlotTrainGraphic(8, 0, Display);
533  PlotTrainGraphic(9, 1, Display);
534 
537 
538  // pick up background bitmaps [2] & [3]
539 
542 
543  PlotElement[2] = MidElement;
545  PlotElement[3] = MidElement;
547  PlotTrainGraphic(10, 2, Display);
548  PlotTrainGraphic(11, 3, Display);
549  // Plotted = true; set in PlotTrainGraphic
550  }
551  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
552  Utilities->CallLogPop(652);
553 }
554 
555 // ---------------------------------------------------------------------------
556 void TTrain::UnplotTrain(int Caller)
557 {
558  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
559  if(!Plotted)
560  {
561  return;
562  }
563  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
564 
565  if(Straddle == MidLag)
566  {
567  if(MidElement > -1)
568  {
573  // to force plot of locked route marker, needed once only for the element
574  }
575  if(LagElement > -1)
576  {
581  // to force plot of locked route marker, needed once only for the element
582  }
583  }
584  else if(Straddle == LeadMidLag)
585  {
586  if(LeadElement > -1)
587  {
590  // to force plot of locked route marker, needed once only for the element
591  }
592  if(MidElement > -1)
593  {
598  // to force plot of locked route marker, needed once only for the element
599  }
600  if(LagElement > -1)
601  {
604  // to force plot of locked route marker, needed once only for the element
605  }
606  }
607  else if(Straddle == LeadMid)
608  {
609  if(LeadElement > -1)
610  {
615  // to force plot of locked route marker, needed once only for the element
616  }
617  if(MidElement > -1)
618  {
623  // to force plot of locked route marker, needed once only for the element
624  }
625  }
626  if(LeadElement > -1)
627  {
629  }
630  if(MidElement > -1)
631  {
633  }
634  if(LagElement > -1)
635  {
637  }
638  Plotted = false;
640  Display->Update();
641  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
642  // resurrected when Update() dropped from PlotOutput etc
643  Utilities->CallLogPop(653);
644 }
645 
646 // ----------------------------------------------------------------------------
647 
648 void TTrain::UpdateTrain(int Caller)
649 /*
650  Note: Some changes made since comments written
651 
652  Brief:
653  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
654  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
655  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
656  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
657  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
658  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
659  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
660  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
661  changed to MidLag within the function and all elements moved down one, old Mid becomes
662  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
663  incremented to reflect the position the train now occupies.
664 
665  Detail:
666  Set TrainFailurePending if all conditions met
667  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
668  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
669  and return.
670  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
671  If there's a LagElement (there will be but include check for good practice - next
672  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
673  train fully on offending point - Derail set and DerailPanding reset, train background
674  colour changed (note that BackgroundColour is a property of the train itself) then return.
675  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
676  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
677  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
678  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
679  if LeadElement is a fouled trailing point.
680  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
681  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
682  replotting the last background segment and checking whether the element is a bridge or crossover with the other
683  track in a route, in which case the route colour is replotted.
684  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
685  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
686  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
687  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
688  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
689  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
690  train can be deleted by the calling function, and the function returns.
691  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
692  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
693  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
694  basic red aspect.
695 
696  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
697  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
698  regardless of direction, and with the correct front code colour.
699 
700  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
701  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
702  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
703  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
704  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
705  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
706  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
707 
708  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
709  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
710  changed similarly. The function then returns.
711 
712  If Crashed is not set then Straddle is incremented and the function returns.
713 */
714 
715 {
716  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
717  UpdateCounter++;
718  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
719  if(UpdateCounter >= 100)
720  {
721  UpdateCounter = 0;
722  }
723  int RandRange = (TrainController->MTBFHours * 3600) / 53;
724 
725  // MTBFHours is in timetable clock hours, min value is 1 & max value is 9,999 (integer values on input)
726  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
727  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
728  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
729  // RandomFailureCounter value is fixed for a full cycle of train updates so this
730  // makes sure there's no bunching of failures as there is for a fixed comparison number
731  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
732  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
733  {
734  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
735  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
736  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
737  // don't fail if:
738  // (a) on a continuation (entering or leaving);
739  // (b) already failed;
740  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
741  // (d) train terminated;
742  // (e) crashed or derailed; or
743  // (f) under signaller control and stopped.
744  // (g) TreatPassAsTimeLocDeparture is true //added at v2.12.0
745  {
746  if((random(RandRange) == 0) && !TreatPassAsTimeLocDeparture)
747  // max value for RandRange is over 2x10^9
748  {
749  // here if failure due
750  TrainFailurePending = true;
751  // fail when PlotElements set to proper Lead & Mid Elements
752  }
753  }
754  }
755 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
756  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
757  {
758  StoppedWithoutPower = true;
759  }
760 */
761 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
762 // THVShortPair ExitPair; //added at v2.10.0
763  int LockedVectorNumber;
764  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
765  // default values - these needed for route checker below
766  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
767 
769  {
771  }
772  if(Crashed || Derailed)
773  {
775  {
776  PlotTrain(7, Display);
777  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
778  Display->Update();
779  }
780  OpTimeToAct = 0.0;
781  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
782  Utilities->CallLogPop(1017);
783  return; // no further action, user has to remove or work around
784  }
786  {
788  }
790  {
792  }
794  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
795  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
796  // to move & then stop again at the same station
797  {
798  TimeTimeLocArrived = false;
799  }
800  if(!Stopped() && !SPADFlag && !TrainFailed)
801  {
803  }
804  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
805  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
806 /* old version where force a stop at buffers regardless of speed
807  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
808  else StoppedAtBuffers = false;
809 */
810 
811  // new version where crash if run into buffers
812  if(!Crashed)
813  {
814  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
815  {
816  if(ExitSpeedFull > 1)
817  {
818  Crashed = true;
822  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
823  // no need for missed action logs - will be sent when train removed
824  StoppedAtBuffers = false;
825  }
827  // stopped at location & stopped without power take precedence
828  {
829  StoppedAtBuffers = true;
830  }
831  else
832  {
833  StoppedAtBuffers = false;
834  }
835  }
836  else
837  {
838  StoppedAtBuffers = false;
839  }
840  }
841  else
842  {
843  StoppedAtBuffers = false;
844  }
845  // if crashed don't want stopped at buffers set
846 
847  // also crash if run into a level crossing that is changing or has barriers up
848  if(!Crashed)
849  {
850  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
851  {
852  int H = Track->TrackElementAt(873, LeadElement).HLoc;
853  int V = Track->TrackElementAt(874, LeadElement).VLoc;
854  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
855  {
856  Crashed = true;
860  // no need for missed action logs - will be sent when train removed
861  }
862  }
863  }
865  {
867  }
868  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
870  //if Command == "" then either TimeLoc or TimeTimeLoc so don't hold, and added last part at v2.12.0 so don't hold if have both command == pas and Treat... flag
871  {
872  HoldAtLocationInTTMode = true;
873  }
874  else if(TrainMode == Timetable)
875  {
876  HoldAtLocationInTTMode = false;
877  }
878  // in Signaller mode HoldAtLocationInTTMode not changed
879 
880  // check if departure pending & set times unless already set
881  if(TrainMode == Timetable)
882  {
884  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
885  {
886  if((ActionVectorEntryPtr->Command != "pas") && (ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && (ActualArrivalTime > TDateTime(0)))
887  {
888  AnsiString ReasonArray[24] = {"a driver is awaited","a guard is awaited","of a medical emergency","of a technical problem","of a security issue",
889  "of a safety issue","of a disturbance","a train crew member has been taken ill","the driver has been taken ill","the guard has been taken ill",
890  "a report has been received concerning safety","a shoe has been lost under the train","of a reported theft",
891  "of an incident involving an animal","some luggage has been lost under the train","a minor repair is needed","a suspicious object has to be dealt with safely",
892  "a door is stuck open","additional stock has to be attached","a security alert","of a train fault","of an operating incident","safety checks are required",
893  "of a shortage of on train crew"};
894  //(ActionVectorEntryPtr->Command != "pas") added at v2.13.0 to rule out passes, though probably not needed
895  //(ActualArrivalTime > TDateTime(0)) added at v2.13.0 to ensure that it has been set and to dismiss trains that are present
896  //at start or have no departure time set.
897  NewDelay = 0; //section relating to random delays added at v2.13.0
898  TDateTime TimetableReleaseTime = TrainController->GetRepeatTime(0, ActionVectorEntryPtr->DepartureTime, RepeatNumber, IncrementalMinutes); //Timetable value
899  TDateTime DwellTime = TimetableReleaseTime - ActualArrivalTime; //Timetable value
900  if(DwellTime < TDateTime(30.0 / 86400))
901  {
902  DwellTime = TDateTime(30.0 / 86400); //Timetable value inc min dwell time
903  }
904  int random = rand() % 10000;
905  if((random != 0))
906  {
907  if(Utilities->DelayMode == Minor)
908  {
909  if(random < 2000)
910  {
911  NewDelay = 1.5 * log(float(2000)/random);//minutes
912  }
913  }
914  else if(Utilities->DelayMode == Moderate)
915  {
916  if(random < 3000)
917  {
918  NewDelay = 3 * log(float(3000)/random);
919  }
920  }
921  else if(Utilities->DelayMode == Major)
922  {
923  if(random < 3500)
924  {
925  NewDelay = 6 * log(float(3500)/random);
926  }
927  }
928  }
929  if(NewDelay < 1)
930  {
931  NewDelay = 0;
932  }
933  if(NewDelay < double(DwellTime) * 1440) //if less than scheduled dwell time then no additional delay
934  {
935  NewDelay = 0;
936  }
937  else
938  {
939  NewDelay -= double(DwellTime) * 1440;//reduce delay by dwell time
940  }
941  if(DelayedRandMins > 0)
942  {
943  DelayedRandMins -= double(DwellTime) * 1440;//reduce knock-on random delay by dwell time
944  }
945  if(DelayedRandMins < 0)
946  {
947  DelayedRandMins = 0;//can't be less than zero
948  }
950  {
951  NewDelay -= DelayedRandMins; //NewDelay is the additional delay over and above the existing knock-on delay (from earlier random delays)
952  //the formula above already includes knock-on effects
953  DelayedRandMins += NewDelay; //the new total delay, knock-on + additional
954 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //don't add here, add when depart, else this value can be > late mins
955  }
956  else
957  {
958  NewDelay = 0;
959 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //as above
960  }
961  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400); //earliest possible release time
962  if(ReleaseTime < ActualArrivalTime + TDateTime(NewDelay / 1440))
963  {
964  ReleaseTime = ActualArrivalTime + TDateTime(NewDelay / 1440); //only add the additional delay
965  }
966  if(ReleaseTime < TimetableReleaseTime)
967  {
968  ReleaseTime = TimetableReleaseTime;
969  }
970  if(DelayedRandMins > double(ReleaseTime - TimetableReleaseTime) * 1440)
971  {
972  DelayedRandMins = double(ReleaseTime - TimetableReleaseTime) * 1440; //reduce this if time has been made up
973  }
974  if(int(NewDelay) > 0) //additional delay over and above knock-on effects from earlier random delays
975  {
976  if(int(NewDelay) == 1)
977  {
979  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
980  }
981  else
982  {
983  if(NewDelay >= 10) //add warning if > 10 mins
984  {
985  int random2 = rand() % 24; //24 reasons
986  AnsiString Reason = ReasonArray[random2];
987  Display->WarningLog(11, HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " +
988  AnsiString(int(NewDelay)) + " minutes");
990  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
991  " minutes because " + Reason);
992  }
993  else
994  {
996  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
997  " minutes because of a minor problem");
998  }
999  }
1000  }
1001  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1002  ActualArrivalTime = TDateTime(0); //only run through this section once per arrival
1003  DepartureTimeSet = true;
1004  }
1005  else if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) //as was, for trains that don't have an errival time set
1006  {//if have skipped to a new service then DepartureTime will be set (in above segement when earlier train arrived)
1007  //but ArrivalTime won't be set as it is reset to 0 at end of above segement when earlier train arrived, so this segement
1008  //will run without any new random delays which might cause additional complications from mixing modifications
1009  NewDelay = 0;
1011  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1012  {
1013  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1014  }
1015  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1016  DepartureTimeSet = true;
1017  }
1018  else if((ActionVectorEntryPtr->Command == "pas") && TreatPassAsTimeLocDeparture) //new segment at v2.12.0 to treat a pass as a departure
1019  {//for when skip to a new service at a pass location. As above this also avoids new random delays, and will avoid above segment because
1020  //departure time isn't set - it's an event time. Again random delays in this situation might cause additional complications
1021  //from mixing modifications.
1022  NewDelay = 0;
1024  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1025  {
1026  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1027  }
1028  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1029  DepartureTimeSet = true;
1030  }
1031  }
1032  }
1033  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
1034  {
1035  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
1036 // this->TimeToExit = TimeToExit; don't need these as values updated directly
1037 // this->ExitPair = ExitPair;
1038  // calculate every 1 sec (in real time, not timetable time) for all trains
1039  }
1040  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
1041  if(TrainMode == Timetable)
1042  {
1043  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
1044  {
1045  RemainHereLogNotSent = true;
1046  }
1048  {
1049  // ignore TimeLoc & TTLoc departures
1050  // Action logs given in functions
1052  LastActionTime + TDateTime(30.0 / 86400)))
1053  {
1054  if(ActionVectorEntryPtr->Command == "fsp")
1055  {
1056  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1057  FrontTrainSplit(0);
1058  if(TrainFailurePending) // ok, stopped so PlotElements set
1059  {
1060  TrainHasFailed(0);
1061  }
1062  Utilities->CallLogPop(2041);
1063  return;
1064  }
1065  else if(ActionVectorEntryPtr->Command == "rsp")
1066  {
1067  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1068  RearTrainSplit(0);
1069  if(TrainFailurePending) // ok, stopped so PlotElements set
1070  {
1071  TrainHasFailed(1);
1072  }
1073  Utilities->CallLogPop(2042);
1074  return;
1075  }
1076  else if(ActionVectorEntryPtr->Command == "Fjo")
1077  {
1078  FinishJoin(0);
1079  }
1080  else if(ActionVectorEntryPtr->Command == "jbo")
1081  {
1082  JoinedBy(0);
1083  }
1084  else if(ActionVectorEntryPtr->Command == "cdt")
1085  {
1086  ChangeTrainDirection(0, false);
1087  }
1088  else if(ActionVectorEntryPtr->Command == "Fns")
1089  {
1090  NewTrainService(0, false);
1091  }
1092  else if(ActionVectorEntryPtr->Command == "Frh")
1093  {
1094  RemainHere(0);
1095  }
1096  else if(ActionVectorEntryPtr->Command == "Fer")
1097  {
1098  TimetableFinished = true;
1099  }
1100  // other aspects of 'Fer' dealt with in TTrain::HasTrainGone()
1101  else if(ActionVectorEntryPtr->Command == "F-nshs")
1102  {
1104  }
1105  else if(ActionVectorEntryPtr->Command == "Frh-sh")
1106  {
1107  RepeatShuttleOrRemainHere(0, false);
1108  }
1109  else if(ActionVectorEntryPtr->Command == "Fns-sh")
1110  {
1112  }
1113 /*
1114  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
1115  shuttle headcode (no train creation)
1116  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1117  remain here
1118  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1119  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
1120 */
1121  }
1122  }
1123  else
1124  {
1126  {
1128  }
1129  }
1130  }
1131  if(TrainMode == Timetable)
1132  {
1133  if(StoppedAtBuffers)
1134  {
1135  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
1136  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
1137  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
1138  if(BufferLocation == "")
1139  {
1141  }
1142  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1143  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1144  {
1148  {
1150  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1151  // Drop missed actions so user can still use sig mode to get back on track
1153  }
1154  if(TrainFailurePending) // ok, stopped so PlotElements set
1155  {
1157  TrainHasFailed(2);
1158  }
1159  Utilities->CallLogPop(1020);
1160  return;
1161  }
1162  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !RevisedStoppedAtLoc() && (TrainController->TTClockTime >
1163  ReleaseTime))
1164  {
1167  {
1170  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1171  // Drop missed actions so user can still use sig mode to get back on track
1173  }
1174  if(TrainFailurePending) // ok, stopped so PlotElements set
1175  {
1177  TrainHasFailed(3);
1178  }
1179  Utilities->CallLogPop(1397);
1180  return;
1181  }
1182  }
1183  else
1184  {
1186  }
1187  }
1188  else
1189  {
1191  }
1192  if(TrainMode == Timetable)
1193  {
1195  {
1197  }
1199  {
1201  }
1202  }
1203  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1204  // restart after stopped for train in front
1205  int NextElementPosition, NextEntryPos;
1206 
1207  if(LeadElement > -1) // if an exit continuation then not set
1208  {
1209  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1210  {
1212  }
1213  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1214  {
1215  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1216  {
1217  LeadExitPos = 1;
1218  }
1219  else
1220  {
1221  LeadExitPos = 3;
1222  }
1223  }
1224  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1225  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1226  }
1227  else
1228  {
1229  NextElementPosition = -1;
1230  NextEntryPos = -1;
1231  }
1232  if((NextElementPosition > -1) && (NextEntryPos > -1))
1233  // may be buffers or continuation so need this check
1234  {
1235 /*
1236  Check whether calling-on conditions met:-
1237  a) approaching train has stopped at a signal but not at a location;
1238  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1239  change of direction (cdt), remaining here (Frh), or under signaller control);
1240  c) at least 1 platform available for the approaching train;
1241  d) points (if any) set for direct route into platform;
1242  e) approaching train is to stop at station;
1243  f) no more facing signals between train and platform;
1244  g) [dropped g]
1245  h) train in front preventing route being set far enough to release stop signal;
1246  i) train in front not exiting at continuation;
1247  j) signal must be within 4km of the stop platform;
1248  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1249  l) no existing route conflicts with the route into the platform; and
1250  m) not failed or without power (these added at v2.10.0)
1251  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1252  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1253 */
1254  if(TrainMode == Timetable)
1255  {
1256  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1257  {
1258  CallingOnFlag = true;
1259  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1260  }
1261  else
1262  {
1263  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1264  {
1265  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1266  {
1268  }
1269  }
1270  CallingOnFlag = false;
1271  }
1272  }
1273  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !RevisedStoppedAtLoc())
1274  {
1275  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1276  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1277  // sets StoppedAtSignal again & train doesn't move
1278  StoppedAtSignal = false;
1279  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1280  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1281  // LeadMidLag and front of train was on LeadElement (after the current move)
1283  EntrySpeed = 0;
1285  FirstHalfMove = true;
1286  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1287  // NextElement is the element to be entered
1288  }
1290  {
1291  if(ClearToNextSignal(0))
1292  {
1293  StoppedForTrainInFront = false;
1294  BeingCalledOn = false;
1295  EntrySpeed = 0;
1297  FirstHalfMove = true;
1298  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1299  }
1300  else
1301  {
1302  if(TrainFailurePending) // ok, stopped so PlotElements set
1303  {
1304  TrainHasFailed(4);
1305  }
1306  Utilities->CallLogPop(1097);
1307  return;
1308  }
1309  }
1310  }
1311  if((Straddle == MidLag) && (LeadElement != -1))
1312  // later check only for Straddle == LeadMid, so need this check here for initial train start
1313  {
1315  }
1316 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1317  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1318  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1319  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1320  which could be when start as Snt.
1321  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1322  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1323  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1324  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1325  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1326  reached.
1327  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1328  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1329  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1330  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1331  sending a message to the performancelog.
1332 */
1333 
1334  if(TrainMode == Timetable)
1335  {
1337  {
1338  if(BeingCalledOn)
1339  {
1340  StoppedForTrainInFront = true;
1341  }
1343  {
1345  }
1347  {
1348  // value updated at every scheduled departure & arrival
1350  AnsiString StationName;
1352  {
1354  }
1356  {
1358  }
1359  else
1360  {
1361  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1362  }
1363  EntrySpeed = 0;
1367  FirstHalfMove = true;
1368  StoppedAtLocation = false;
1369 
1370  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1371  {
1372  StoppedWithoutPower = true;
1373  }
1374  if((NextElementPosition > -1) && (NextEntryPos > -1))
1375  // condition check added for SloughIECC error reported by James U
1376  {
1377  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1378  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1379  {
1380  StoppedAtSignal = true;
1381  if(!StoppedWithoutPower)
1382  // if stopped without power just keep existing background colour
1383  {
1385  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1386  }
1387  }
1388  }
1390  {
1391  TimeTimeLocArrived = false;
1392  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1393  // no warning for TimeTimeLoc departure
1394  }
1395  else if(TreatPassAsTimeLocDeparture) //added at v2.12.0 so late/early/on time mins recorded accurately
1396  {
1397  LogAction(36, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->EventTime, ActionVectorEntryPtr->Warning); //EventTime because the real event is a pass
1398  }
1399  else //must be TimeLoc departure
1400  {
1402  }
1403  TreatPassAsTimeLocDeparture = false; //added at v2.12.0, reset after train departs
1404  DepartureTimeSet = false;
1405  // no need to set LastActionTime for a departure
1406  //deal here with departure pointer change, increment if SkippedDeparture
1407  CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //only add these after late mins added (in LogAction)
1408 
1409  if(SkippedDeparture)
1410  {
1413  TrainSkippedEvents = 0;
1414  SkippedDeparture = false;
1415  SkipPtrValue = 0;
1416  ActionsSkippedFlag = false;
1417  }
1418  else
1419  {
1421  }
1422  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1423  // note
1424 /*
1425  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1426  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1427  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1428  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1429  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1430  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1431  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1432 */
1434  {
1435  StoppedAtBuffers = true;
1436  }
1437  else if(!StoppedWithoutPower)
1438  // if buffers or no power, don't set values
1439  {
1441  {
1442  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1443  // NextElement is the element to be entered
1444  }
1445  else
1446  {
1448  // use LeadElement for an exit continuation
1449  }
1450  }
1451  }
1452  }
1453  }
1454  if(Straddle == LeadMidLag) //train on a half element
1455  {
1457  {
1458  Utilities->CallLogPop(654);
1459  return;
1460  }
1461  }
1462  else //train fully on 2 elements
1463  {
1465  {
1466  Utilities->CallLogPop(655);
1467  return;
1468  }
1469  }
1470  if((LeadElement > -1) && (MidElement > -1))
1471  {
1473  {
1474  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1475  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1476  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1477  SignallerStoppingFlag = false;
1478  StepForwardFlag = false;
1479  }
1480  }
1481  if(Stopped())
1482  // this is what prevents another movement if the train is stopped
1483  {
1484  if(TrainFailurePending) // ok, stopped so PlotElements set
1485  {
1486  TrainHasFailed(5);
1487  }
1488  BrakeRate = 0;
1489  Utilities->CallLogPop(656);
1490  return;
1491  }
1492 
1493  // HERE WHEN READY FOR NEXT MOVE
1494 
1495  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1496  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1497  if(Straddle == LeadMid) //fully on 2 elements
1498  {
1499  if(LeadElement > -1)
1500  {
1501  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1502  {
1504  if(TIF.TrackType == SignalPost)
1505  {
1506  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1507  int TIFExitPos = 0;
1508  if(TIFEntryPos == 0)
1509  {
1510  TIFExitPos = 1;
1511  }
1512  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1513  {
1514  SPADFlag = true; // user has to intervene to reset & restart after spad
1515  }
1516  }
1517  }
1518  }
1519  }
1520 
1521  // check for train in front & if so stop at next access (when train fully on element next to train)
1522  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1523  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1524  // variable TrainInFrontInSignallerModeFlag
1525  {
1526  if(LeadElement > -1)
1527  {
1528  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1529  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1530  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1531  // true if another train on NextEntryPos track whether bridge or not
1532  {
1533  StoppedForTrainInFront = true;
1534  }
1535  else
1536  {
1537  StoppedForTrainInFront = false;
1538  }
1539  }
1540  }
1541  if((Straddle == LeadMid) && SPADFlag)
1542  // give message + plot background when ready to move half past the signal
1543  {
1544  if(NextElementPosition > -1)
1545  {
1546  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1547  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1548  {
1549  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1551  // if goes past 2 signals then give message twice
1553  }
1554  }
1555  }
1556  if(Straddle == LeadMidLag)
1557  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1558  {
1559  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1560  if(SPADFlag)
1561  {
1562  if(ExitSpeedFull == 0)
1563  {
1564  StoppedAfterSPAD = true;
1565  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1566  }
1567  }
1569  {
1570  if(ExitSpeedFull == 0)
1571  {
1572  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1573  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1574  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1575  // is sent at the right time and once only.
1576  SignallerStopped = true;
1577  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1578  StepForwardFlag = false;
1579  SignallerStoppingFlag = false;
1580  TTrackElement TE;
1581  AnsiString Loc = "";
1582  bool LocNamed = false;
1583  if(LeadElement > -1)
1584  {
1585  TE = Track->TrackElementAt(782, LeadElement);
1586  if(TE.ActiveTrackElementName != "")
1587  {
1588  Loc = TE.ActiveTrackElementName;
1589  LocNamed = true;
1590  }
1591  else
1592  {
1593  Loc = "track element " + TE.ElementID;
1594  }
1595  }
1596  if((MidElement > -1) && !LocNamed)
1597  {
1598  TE = Track->TrackElementAt(783, MidElement);
1599  if(TE.ActiveTrackElementName != "")
1600  {
1601  Loc = TE.ActiveTrackElementName;
1602  LocNamed = true;
1603  }
1604  else if(Loc == "")
1605  {
1606  Loc = "track element " + TE.ElementID;
1607  }
1608  }
1609  if(Loc == "")
1610  {
1611  Loc = "outside railway";
1612  // must have stopped after left at a continuation (because both lead & mid == -1)
1613  }
1614  else
1615  {
1616  Loc = "at " + Loc;
1617  }
1618  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1619  }
1620  }
1621  if(LeadElement > -1) // if an exit continuation then not set
1622  {
1623  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1624  {
1626  }
1627  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1628  {
1629  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1630  {
1631  LeadExitPos = 1;
1632  }
1633  else
1634  {
1635  LeadExitPos = 3;
1636  }
1637  }
1638  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1639  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1640  }
1641  else
1642  {
1643  NextElementPosition = -1;
1644  NextEntryPos = -1;
1645  }
1648  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1649 
1650  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1651  {
1652  StoppedWithoutPower = true;
1653  }
1654  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1655  // may be buffers or continuation. SPADFlag added at v2.1.0
1656  // so don't override the SPAD colour & don't set StoppedAtSignal
1657  {
1658  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1659  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !RevisedStoppedAtLoc())
1660  {
1661  StoppedAtSignal = true;
1662  if(!StoppedWithoutPower)
1663  // leave background as is if no power, but set StoppedAtSignal
1664  {
1666  }
1667  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1668  }
1669  }
1670  if(!Stopped())
1671  {
1672  if((NextElementPosition > -1) && (NextEntryPos > -1))
1673  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1674  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1675  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1676  // function for fuller explanation
1677  {
1678  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1679  // NextElement is the element to be entered
1680  }
1681  // follow the continuation exits:-
1682  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1683  {
1685  // Use LeadElement for calcs if lead is a continuation
1686  }
1687  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1688  {
1690  // Use MidElement for calcs if mid is a continuation
1691  }
1692  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1693  {
1695  // Use LagElement for calcs if lag is a continuation
1696  }
1697  }
1698  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1699  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1700  // Trains may not be in a route
1701  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1702  {
1703  // NB if LeadElement == -1 then the above test returns false
1704  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1705  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1706  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1707  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1708  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1709  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1710  FirstPair.second).GetELink() == TempELink))
1711  {
1712  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1713  }
1714  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1715  SecondPair.second).GetELink() == TempELink))
1716  {
1717  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1718  }
1719  }
1720  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1721  // Trains may not be in a route
1722  {
1723  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1724  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1725  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1726  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1727  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1728  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1729  FirstPair.second).GetELink() == TempELink))
1730  {
1731  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1732  }
1733  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1734  SecondPair.second).GetELink() == TempELink))
1735  {
1736  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1737  }
1738  }
1739  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1740  // Trains may not be in a route
1741  {
1742  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1743  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1744  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1745  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1746  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1747  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1748  FirstPair.second).GetELink() == TempELink))
1749  {
1750  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1751  }
1752  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1753  SecondPair.second).GetELink() == TempELink))
1754  {
1755  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1756  }
1757  AllRoutes->CheckMapAndRoutes(8); // test
1758  }
1759  if(LagElement > -1)
1760  // not entering at a continuation so can deal with train leaving the lag element
1761  {
1763  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1764  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1765  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1766 
1767  TPrefDirElement PrefDirElement;
1768  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1769  // as this is a 16x16 graphic
1771  {
1773  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1774  }
1775  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1776  {
1777  int RouteNumber;
1778  TrainGone = true;
1779  // flag to indicate train to be deleted - outside this function
1781  {
1782  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1783  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1784  // calc distance from & inc last signal to exit
1785  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1786  int NewLastElement = 0, NewLastExitPos = 0;
1787  // need above because can't change LastElement & LastExitPos until both new values obtained
1788  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1789  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1790  LastElement).TrackType != Points))
1791  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1792  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1793  // leave CumDistance as it was in these circumstances.
1794  {
1795  if(LastExitPos < 2)
1796  {
1797  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1798  }
1799  else
1800  {
1801  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1802  }
1803  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1804  if(NewLastElement == -1)
1805  // this will catch buffers or any other connection failure
1806  {
1807  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1808  }
1809  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1810  if(NewLastExitPos == -1)
1811  {
1812  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1813  }
1814  LastElement = NewLastElement;
1815  LastExitPos = NewLastExitPos;
1816  }
1817  // if at signal add this in too
1818  if(CumDistance < 1200)
1819  {
1820  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1821  }
1822  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1823  // else use 1200m - CumDistance
1824  int FirstDistance = 0;
1825  if(CumDistance >= 1200)
1826  {
1827  FirstDistance = 100;
1828  }
1829  else
1830  {
1831  FirstDistance = 1200 - CumDistance;
1832  }
1833  if(FirstDistance < 100)
1834  {
1835  FirstDistance = 100; // don't allow < 100
1836  }
1837  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1838  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1839  if(ExitSpeedFull > 20.0)
1840  {
1841  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1842  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1843  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1844  // 4320.0 = 3.6 * 1200, .0 to make it a double
1845  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1846  }
1847  else
1848  {
1849  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1850  ContinuationAutoSigEntry.SecondDelay = 120.0;
1851  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1852  }
1853  ContinuationAutoSigEntry.AccessNumber = 0;
1854  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1856  {
1858  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1859  VectorIT++)
1860  {
1861  if(VectorIT->RouteNumber == RouteNumber)
1862  {
1863  // another train has passed out of same route so erase earlier entry
1864  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1865  break;
1866  }
1867  }
1868  }
1869  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1870  }
1872  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1873  Display->Update();
1874  // need to keep this since Update() not called for PlotSmallOutput as too slow
1875  Utilities->CallLogPop(659);
1876  return;
1877  }
1878  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1879  if(LeadElement > -1)
1880  {
1882  // changed to lead so reset early
1883  {
1884  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1886  // don't plot if zoomed out
1887  if(!Display->ZoomOutFlag)
1888  {
1890  }
1891  // covers signal resetting in same direction
1892  }
1893  }
1895  {
1896  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1897  {
1898  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1899  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1900  TPrefDirElement PrefDirElement;
1901  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1903  {
1905  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1906  }
1908  LockedVectorNumber)))
1909  {
1911  }
1912  }
1913  }
1914  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1915  {
1916  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1918  // don't plot if zoomed out
1919  if(!Display->ZoomOutFlag)
1920  {
1922  }
1923  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1924  }
1926  {
1927  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1928  {
1929  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1930  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1931  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1932  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1933  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1934  int RouteNumber;
1936  // already know it's an autosigsroute, this is just to get the RouteNumber
1937  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1938  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1939  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1940  int RouteNumber2;
1942  // already know it's an autosigsroute, this is just to get the RouteNumber
1943  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1944  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1945  {
1946  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1947  // this was in the 1.3.0 addition but without the condition
1948  }
1949  // end of 1.3.2 addition
1950  // end of 1.3.0.addition
1951  }
1952  TPrefDirElement PrefDirElement;
1953  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1955  {
1957  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1958  }
1959  }
1960  }
1961  }
1962  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1963  if(Straddle == LeadMid)
1964  {
1965  AllowedToPassRedSignal = false;
1966  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1967  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1968  if(DerailPending)
1969  // set during last GetLeadElement, but only act on it when train fully on offending point
1970  // i.e. next time Straddle reaches LeadMid
1971  {
1972  Derailed = true;
1973  DerailPending = false;
1977  Utilities->CallLogPop(657);
1978  return;
1979  }
1986  Straddle = MidLag;
1987  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1988  // LeadElement during this function (note that if stopped at signal then won't get this far)
1989  if(LeadElement > -1)
1990  {
1992  // i.e an exit continuation only
1993  // if don't exclude entry continuations then can't progress past it
1994  {
1995  LeadElement = -1;
1996  }
1997  else
1998  {
1999  GetLeadElement(0);
2000  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
2002  if(Stopped())
2003  {
2004  if(TrainFailurePending) // ok, stopped so PlotElements set
2005  {
2006  TrainHasFailed(6);
2007  }
2008  Utilities->CallLogPop(658);
2009  return; // i.e. don't move forward one step if next element is a red signal
2010  }
2011  }
2012  }
2013  }
2014  if(LagElement > -1)
2015  {
2016  // below are the actions required at both half moves for LagElement > -1
2018 
2019  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
2020  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
2021  // need to do this for each half element
2022 
2023  TPrefDirElement PrefDirElement;
2024  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
2025  {
2026  int RouteNumber; // holder for call below - not used
2028  {
2029  if(Utilities->clTransparent == TColor(0xFFFFFF))
2030  // change to black for a white background
2031  {
2033  // only applies for AutoSigs Route in case was locked & timed out
2034  }
2035  else
2036  // change to white for a dark background
2037  {
2039  // only applies for AutoSigs Route in case was locked & timed out
2040  }
2042  }
2043  }
2045  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
2046  // or a train on the opposite track - needs to be replotted
2047  }
2048  // update all array values
2049  HOffset[3] = HOffset[2];
2050  HOffset[2] = HOffset[1];
2051  HOffset[1] = HOffset[0];
2052  VOffset[3] = VOffset[2];
2053  VOffset[2] = VOffset[1];
2054  VOffset[1] = VOffset[0];
2055  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
2056 
2057  BackgroundPtr[3] = BackgroundPtr[2];
2058  BackgroundPtr[2] = BackgroundPtr[1];
2059  BackgroundPtr[1] = BackgroundPtr[0];
2060  BackgroundPtr[0] = TempPtr;
2061 
2062  // update headcode graphics depending on Lead entry value
2063  if(LeadElement > -1) // if Lead is -1 then stays as is
2064  {
2066  {
2067  for(int x = 0; x < 4; x++)
2068  {
2069  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
2070  }
2071  }
2072  else
2073  {
2074  for(int x = 0; x < 4; x++)
2075  {
2077  }
2078  }
2079  }
2080  if(TrainMode == Timetable)
2081  {
2083  }
2084  else
2085  {
2087  }
2089 
2090  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
2091  if(LeadElement > -1)
2092  {
2093  if(Straddle == MidLag)
2094  // just about to move half onto the new lead element
2095  {
2097  // pick up new background bitmap [0]
2099  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
2100  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
2101  // check if own ID for entry at continuation, else crashes into itself!
2102  {
2103  // OK if crossing on a bridge
2104  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
2105  if(OtherTrainEntryPos == -1)
2106  {
2107  throw Exception("Error - OtherTrainEntryPos not set");
2108  }
2109  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
2110  // LeadEntryPos for rear end crashes
2111  (LeadExitPos == OtherTrainEntryPos))
2112  // LeadExitPos for head-on crashes
2113  {
2115  Crashed = true; // only set if Straddle = MidLag
2116  CallingOnFlag = false;
2117  // in case was set, need to disable call on if call on button had been pressed
2118  }
2119  }
2120  else if(MidElement > -1) // will be -1 for continuation entries
2121  {
2122  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
2123  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
2124  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
2125  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
2126  int OtherTrainID = -1;
2127  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
2128  {
2129  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
2130  {
2131  TrainCrashedInto = OtherTrainID;
2132  Crashed = true; // only set if Straddle = MidLag
2133  CallingOnFlag = false;
2134  // in case was set, need to disable call on if call on button had been pressed
2135  }
2136  }
2137  }
2138  }
2139  else
2140  {
2142  // pick up new background bitmap [0]
2144  }
2145  PlotElement[0] = LeadElement;
2147  PlotTrainGraphic(12, 0, Display);
2148  }
2149  if(MidElement > -1)
2150  {
2151  PlotElement[2] = MidElement;
2153  PlotTrainGraphic(1, 2, Display);
2154  }
2155  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2156  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2157  if(Straddle == MidLag)
2158  {
2159  if(MidElement > -1)
2160  {
2161  PlotElement[1] = MidElement;
2163  PlotTrainGraphic(2, 1, Display);
2164  }
2165  if(LagElement > -1)
2166  {
2167  PlotElement[3] = LagElement;
2169  PlotTrainGraphic(3, 3, Display);
2170  }
2171  }
2172  else // Straddle == LeadMidLag
2173  {
2174  if(LeadElement > -1)
2175  {
2176  PlotElement[1] = LeadElement;
2178  PlotTrainGraphic(4, 1, Display);
2179  }
2180  if(MidElement > -1)
2181  {
2182  PlotElement[3] = MidElement;
2184  PlotTrainGraphic(5, 3, Display);
2185  }
2186  }
2187  if(Crashed)
2188  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2189  {
2194  // in case was set, need to disable call on if call on button had been pressed
2201  Straddle = LeadMidLag;
2202  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2203  Display->Update();
2204  // resurrected when Update() dropped from PlotOutput etc
2205  Utilities->CallLogPop(660);
2206  return;
2207  }
2208  // deal here with station stops & pass times after all replotting done but before Straddle changed
2209  if(TrainMode == Timetable)
2210  {
2211  if(Straddle == LeadMidLag)
2212  {
2213  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2214  {
2215  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2216  // to point to the location arrival entry - before a change of direction
2217  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2218  bool StopRequired = false;
2219  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2220  if(TTVPos > -1) // -1 if can't find it or if name is ""
2221  {
2222  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2223  // or a station where next element contains a train or a stop signal, if so
2224  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2225  // to test the actual track the train is on since it can't be a platform
2226  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2227  TTrackElement NextTrackElement; // default for now
2228  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2229  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2230  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2231  int NextElementEntryPos = -1;
2232  int NextElementExitPos = -1;
2233  bool TrainOnNextElement = false;
2234  bool StopSignalAtNextElement = false;
2235  if(ForwardConnection)
2236  // if no forward connection can't derive anything from it without errors
2237  {
2238  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2239  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2240  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2241  // this is only for signals so no need to worry about points ambiguity
2242  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2243  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2244  }
2245  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2246  {
2247  if(TTVPos > 0)
2248  {
2250  ActionVectorEntryPtr += TTVPos;
2251  }
2252  if(StopRequired)
2253  {
2254  StoppedAtLocation = true;
2255  StoppedAtSignal = false;
2256  // may have been set earlier at line 925 so need to reset as
2257  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2258  // in zoom out mode
2259  if(!TrainFailed)
2260  {
2262  // pale green
2263  }
2265  ActualArrivalTime = TrainController->TTClockTime; //added at v2.13.0
2267  {
2268  TimeTimeLocArrived = true;
2269  // used in case of later signaller control, when need to know
2270  // whether had arrived or not, to avoid sending the arrival
2271  // message twice, see TInterface::TimetableControl1Click
2272  }
2273  }
2274  else
2275  {
2277  }
2279  {
2281  }
2282  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2284  }
2285  }
2286  }
2287  }
2288  }
2289  if(Straddle == MidLag)
2290  {
2291  Straddle = LeadMidLag;
2292  FirstHalfMove = false;
2293  }
2294  else if(Straddle == LeadMidLag)
2295  {
2296  Straddle = LeadMid;
2297  FirstHalfMove = true;
2298  }
2299  else if(Straddle == LeadMid)
2300  {
2301  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2302  }
2303  if(TrainFailurePending) // ok, moving but PlotElements set above
2304  {
2305  TrainHasFailed(7);
2306  }
2307  Display->Update();
2308  // need to keep this since Update() not called for PlotSmallOutput as too slow
2309  Utilities->CallLogPop(661);
2310 }
2311 
2312 // ----------------------------------------------------------------------------
2313 
2314 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2315 {
2316  switch(CodeChar)
2317  {
2318  case '0':
2319  return(RailGraphics->Code0);
2320 
2321  case '1':
2322  return(RailGraphics->Code1);
2323 
2324  case '2':
2325  return(RailGraphics->Code2);
2326 
2327  case '3':
2328  return(RailGraphics->Code3);
2329 
2330  case '4':
2331  return(RailGraphics->Code4);
2332 
2333  case '5':
2334  return(RailGraphics->Code5);
2335 
2336  case '6':
2337  return(RailGraphics->Code6);
2338 
2339  case '7':
2340  return(RailGraphics->Code7);
2341 
2342  case '8':
2343  return(RailGraphics->Code8);
2344 
2345  case '9':
2346  return(RailGraphics->Code9);
2347 
2348  case 'A':
2349  return(RailGraphics->CodeA);
2350 
2351  case 'B':
2352  return(RailGraphics->CodeB);
2353 
2354  case 'C':
2355  return(RailGraphics->CodeC);
2356 
2357  case 'D':
2358  return(RailGraphics->CodeD);
2359 
2360  case 'E':
2361  return(RailGraphics->CodeE);
2362 
2363  case 'F':
2364  return(RailGraphics->CodeF);
2365 
2366  case 'G':
2367  return(RailGraphics->CodeG);
2368 
2369  case 'H':
2370  return(RailGraphics->CodeH);
2371 
2372  case 'I':
2373  return(RailGraphics->CodeI);
2374 
2375  case 'J':
2376  return(RailGraphics->CodeJ);
2377 
2378  case 'K':
2379  return(RailGraphics->CodeK);
2380 
2381  case 'L':
2382  return(RailGraphics->CodeL);
2383 
2384  case 'M':
2385  return(RailGraphics->CodeM);
2386 
2387  case 'N':
2388  return(RailGraphics->CodeN);
2389 
2390  case 'O':
2391  return(RailGraphics->CodeO);
2392 
2393  case 'P':
2394  return(RailGraphics->CodeP);
2395 
2396  case 'Q':
2397  return(RailGraphics->CodeQ);
2398 
2399  case 'R':
2400  return(RailGraphics->CodeR);
2401 
2402  case 'S':
2403  return(RailGraphics->CodeS);
2404 
2405  case 'T':
2406  return(RailGraphics->CodeT);
2407 
2408  case 'U':
2409  return(RailGraphics->CodeU);
2410 
2411  case 'V':
2412  return(RailGraphics->CodeV);
2413 
2414  case 'W':
2415  return(RailGraphics->CodeW);
2416 
2417  case 'X':
2418  return(RailGraphics->CodeX);
2419 
2420  case 'Y':
2421  return(RailGraphics->CodeY);
2422 
2423  case 'Z':
2424  return(RailGraphics->CodeZ);
2425 
2426  case 'a':
2427  return(RailGraphics->Code_a);
2428 
2429  case 'b':
2430  return(RailGraphics->Code_b);
2431 
2432  case 'c':
2433  return(RailGraphics->Code_c);
2434 
2435  case 'd':
2436  return(RailGraphics->Code_d);
2437 
2438  case 'e':
2439  return(RailGraphics->Code_e);
2440 
2441  case 'f':
2442  return(RailGraphics->Code_f);
2443 
2444  case 'g':
2445  return(RailGraphics->Code_g);
2446 
2447  case 'h':
2448  return(RailGraphics->Code_h);
2449 
2450  case 'i':
2451  return(RailGraphics->Code_i);
2452 
2453  case 'j':
2454  return(RailGraphics->Code_j);
2455 
2456  case 'k':
2457  return(RailGraphics->Code_k);
2458 
2459  case 'l':
2460  return(RailGraphics->Code_l);
2461 
2462  case 'm':
2463  return(RailGraphics->Code_m);
2464 
2465  case 'n':
2466  return(RailGraphics->Code_n);
2467 
2468  case 'o':
2469  return(RailGraphics->Code_o);
2470 
2471  case 'p':
2472  return(RailGraphics->Code_p);
2473 
2474  case 'q':
2475  return(RailGraphics->Code_q);
2476 
2477  case 'r':
2478  return(RailGraphics->Code_r);
2479 
2480  case 's':
2481  return(RailGraphics->Code_s);
2482 
2483  case 't':
2484  return(RailGraphics->Code_t);
2485 
2486  case 'u':
2487  return(RailGraphics->Code_u);
2488 
2489  case 'v':
2490  return(RailGraphics->Code_v);
2491 
2492  case 'w':
2493  return(RailGraphics->Code_w);
2494 
2495  case 'x':
2496  return(RailGraphics->Code_x);
2497 
2498  case 'y':
2499  return(RailGraphics->Code_y);
2500 
2501  case 'z':
2502  return(RailGraphics->Code_z);
2503 
2504  default:
2505  return(RailGraphics->TempHeadCode);
2506  }
2507 }
2508 
2509 // ----------------------------------------------------------------------------
2510 
2511 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2512 {
2513  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2514  if(Code.Length() != 4)
2515  {
2516  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2517  }
2518  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2519  {
2520  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2521  }
2522  if(BackgroundColour != clB5G5R5)
2523  // i.e. not the basic graphic colour as loaded from resource file
2524  {
2525  for(int x = 0; x < 4; x++)
2526  {
2528  }
2529  }
2530  Utilities->CallLogPop(1484);
2531 }
2532 
2533 // ----------------------------------------------------------------------------
2534 
2535 void TTrain::GetLeadElement(int Caller)
2536 // assumes Mid & Lag already set, sets LeadElement,
2537 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2538 {
2539  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2540  DerailPending = false;
2544  {
2545  // attr 0=straight, - links 0 & 1 (0 = lead)
2546  // attr 1=diverging, - links 2 & 3 (2 = lead)
2547  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2548  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2549 
2550  // if enter at lead, exit at whatever attr set at
2551  // if enter at lag, exit at lead, but set derail wrt attribute
2552  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2553  {
2554  LeadExitPos = 1;
2555  }
2556 
2557  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2558  // best to be on safe side
2559  else if(LeadEntryPos == 0)
2560  {
2561  LeadEntryPos = 2;
2562  LeadExitPos = 3;
2563  }
2564  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2565  {
2566  LeadEntryPos = 0;
2567  LeadExitPos = 1;
2568  }
2569  else if(LeadEntryPos == 2)
2570  {
2571  LeadExitPos = 3;
2572  }
2573 
2574  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2575  {
2576  LeadExitPos = 0;
2577  }
2578  else if(LeadEntryPos == 1)
2579  {
2580  LeadExitPos = 0;
2581  DerailPending = true;
2582  }
2583  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2584  {
2585  LeadExitPos = 0;
2586  DerailPending = true;
2587  }
2588  else if(LeadEntryPos == 3)
2589  {
2590  LeadExitPos = 0;
2591  }
2592  }
2593  else if(LeadEntryPos == 0)
2594  {
2595  LeadExitPos = 1;
2596  }
2597  else if(LeadEntryPos == 1)
2598  {
2599  LeadExitPos = 0;
2600  }
2601  else if(LeadEntryPos == 2)
2602  {
2603  LeadExitPos = 3;
2604  }
2605  else if(LeadEntryPos == 3)
2606  {
2607  LeadExitPos = 2;
2608  }
2609  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2610 /* signal check moved to Update() function
2611  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2612  && (TrackElement.Attribute == 0))//0 = red
2613  {
2614  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2615  }
2616  else
2617  {
2618  StoppedAtSignal = false;
2619  }
2620 */
2621  Utilities->CallLogPop(662);
2622 }
2623 
2624 // ----------------------------------------------------------------------------
2625 
2626 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2627 {
2628  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2629  switch(Link)
2630  {
2631  case 1:
2632  {
2633  HOffset = 0;
2634  VOffset = 0;
2635  break;
2636  }
2637 
2638  case 2:
2639  {
2640  HOffset = 4;
2641  VOffset = 0;
2642  break;
2643  }
2644 
2645  case 3:
2646  {
2647  HOffset = 8;
2648  VOffset = 0;
2649  break;
2650  }
2651 
2652  case 4:
2653  {
2654  HOffset = 0;
2655  VOffset = 4;
2656  break;
2657  }
2658 
2659  case 6:
2660  {
2661  HOffset = 8;
2662  VOffset = 4;
2663  break;
2664  }
2665 
2666  case 7:
2667  {
2668  HOffset = 0;
2669  VOffset = 8;
2670  break;
2671  }
2672 
2673  case 8:
2674  {
2675  HOffset = 4;
2676  VOffset = 8;
2677  break;
2678  }
2679 
2680  case 9:
2681  {
2682  HOffset = 8;
2683  VOffset = 8;
2684  break;
2685  }
2686 
2687  default:
2688  {
2689  throw Exception("Error in GetOffsetValues - Link value wrong");
2690  }
2691  }
2692  Utilities->CallLogPop(674);
2693 }
2694 
2695 // ---------------------------------------------------------------------------
2696 
2697 bool TTrain::LowEntryValue(int EntryLink) const
2698 {
2699 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2700  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2701 */
2702  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2703  {
2704  return(true);
2705  }
2706  else
2707  {
2708  return(false);
2709  }
2710 }
2711 
2712 // ---------------------------------------------------------------------------
2713 
2714 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2715 {
2716  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2717  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2718  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2719  // default values
2720  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2721 
2722  TAllRoutes::TRouteType RouteType;
2723 
2724  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2725 
2726  TRect SourceRect, DestRect;
2727 
2728  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2729  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2730  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2731  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2732 
2733  TempGraphic->PixelFormat = pf8bit;
2734  TempGraphic->Width = 16;
2735  TempGraphic->Height = 16;
2736  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2737 
2738  if(TempElement.TrackType == Points)
2739  {
2740  TempGraphic->Assign(TempElement.GraphicPtr);
2741  TempGraphic->Transparent = true;
2742  TempGraphic->TransparentColor = Utilities->clTransparent;
2743  if(RouteType == TAllRoutes::AutoSigsRoute)
2744  {
2745  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2746  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2747  }
2748  else
2749  {
2750  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2751  }
2752  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2753  }
2754  else if(TempElement.TrackType == GapJump) // plot set gap
2755  {
2756  if(TempElement.SpeedTag == 88)
2757  {
2758  TempGraphic->Assign(RailGraphics->gl88set);
2759  }
2760  else if(TempElement.SpeedTag == 89)
2761  {
2762  TempGraphic->Assign(RailGraphics->gl89set);
2763  }
2764  else if(TempElement.SpeedTag == 90)
2765  {
2766  TempGraphic->Assign(RailGraphics->gl90set);
2767  }
2768  else if(TempElement.SpeedTag == 91)
2769  {
2770  TempGraphic->Assign(RailGraphics->gl91set);
2771  }
2772  else if(TempElement.SpeedTag == 92)
2773  {
2774  TempGraphic->Assign(RailGraphics->gl92set);
2775  }
2776  else if(TempElement.SpeedTag == 93)
2777  {
2778  TempGraphic->Assign(RailGraphics->bm93set);
2779  }
2780  else if(TempElement.SpeedTag == 94)
2781  {
2782  TempGraphic->Assign(RailGraphics->bm94set);
2783  }
2784  else if(TempElement.SpeedTag == 95)
2785  {
2786  TempGraphic->Assign(RailGraphics->gl95set);
2787  }
2788  TempGraphic->Transparent = true;
2789  TempGraphic->TransparentColor = Utilities->clTransparent;
2790  if(RouteType == TAllRoutes::AutoSigsRoute)
2791  {
2792  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2793  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2794  }
2795  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2796  }
2797  // new for version 0.6
2798  else if(TempElement.TrackType == SignalPost)
2799  {
2800  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2801  {
2802  for(int x = 0; x < 40; x++)
2803  {
2804  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2805  // need to stop aspect
2806  {
2807  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2808  break;
2809  }
2810  }
2811  }
2812  else // normal signal
2813  {
2814  TempGraphic->Assign(TempElement.GraphicPtr);
2815  // GraphicPtr set to normal signal in a signal track element
2816  }
2817  TempGraphic->Transparent = true;
2818  TempGraphic->TransparentColor = Utilities->clTransparent;
2819  if(RouteType == TAllRoutes::AutoSigsRoute)
2820  {
2821  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2822  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2823  }
2824  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2825  }
2826  else
2827  {
2828  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2829  // can't name points gaps or signals so 'else' OK
2830  bool FoundFlag;
2831  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2832  if(FoundFlag)
2833  {
2835  {
2836  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2837  TempGraphic->Assign(RailGraphics->bmName);
2838  TempGraphic->Transparent = true;
2839  TempGraphic->TransparentColor = Utilities->clTransparent;
2840  if(RouteType == TAllRoutes::AutoSigsRoute)
2841  {
2842  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2843  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2844  }
2845  else
2846  {
2847  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2848  }
2849  // draw track on top
2850  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2851  }
2852  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2853  {
2854  TempGraphic->Assign(TempElement.GraphicPtr);
2855  TempGraphic->Transparent = true;
2856  TempGraphic->TransparentColor = Utilities->clTransparent;
2857  // note that can't be an AutoSigsRoute
2858  // now overlay the LC central portion
2859  int BDVectorPos = -1; //not used
2860  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2861  {
2862  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2863  }
2864  else
2865  {
2866  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2867  }
2868  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2869  }
2870  else
2871  {
2872  TempGraphic->Assign(TempElement.GraphicPtr);
2873  TempGraphic->Transparent = true;
2874  TempGraphic->TransparentColor = Utilities->clTransparent;
2875  if(RouteType == TAllRoutes::AutoSigsRoute)
2876  {
2877  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2878  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2879  }
2880  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2881  }
2882  }
2883  else
2884  {
2885  TempGraphic->Assign(TempElement.GraphicPtr);
2886  TempGraphic->Transparent = true;
2887  TempGraphic->TransparentColor = Utilities->clTransparent;
2888  if(RouteType == TAllRoutes::AutoSigsRoute)
2889  {
2890  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2891  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2892  }
2893  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2894  }
2895  }
2896  delete TempGraphic;
2897  Utilities->CallLogPop(675);
2898 }
2899 
2900 // ---------------------------------------------------------------------------
2901 
2902 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2903 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2904 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2905 /*
2906 
2907  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2908  {
2909  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2910  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2911  TAllRoutes::TRouteType RouteType;
2912  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2913  // default values
2914  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2915  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2916  TRect SourceRect, DestRect, ScreenSourceRect;
2917  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2918 
2919  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2920  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2921  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2922 
2923  //add text & user graphics if any to *GraphicPtr prior to adding the track
2924  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2925  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2926  int Right = Left + 8;
2927  int Bottom = Top + 8;
2928  ScreenSourceRect.init(Left, Top, Right, Bottom);
2929  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2930  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2931 
2932  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2933  TempGraphic->PixelFormat = pf8bit;
2934  TempGraphic->Width = 16;
2935  TempGraphic->Height = 16;
2936 
2937  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2938  SourceGraphic->PixelFormat = pf8bit;
2939  SourceGraphic->Width = 16;
2940  SourceGraphic->Height = 16;
2941  SourceGraphic->Transparent = true;
2942  SourceGraphic->TransparentColor = Utilities->clTransparent;
2943 
2944  if (TempElement.TrackType == Points)
2945  {
2946  TempGraphic->Assign(TempElement.GraphicPtr);
2947  TempGraphic->Transparent = true;
2948  TempGraphic->TransparentColor = Utilities->clTransparent;
2949  if (RouteType == TAllRoutes::AutoSigsRoute)
2950  {
2951  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2952  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2953  }
2954  else
2955  {
2956  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2957  }
2958  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2959  }
2960  else if (TempElement.TrackType == GapJump) // plot set gap
2961  {
2962  if (TempElement.SpeedTag == 88)
2963  TempGraphic->Assign(RailGraphics->gl88set);
2964  else if (TempElement.SpeedTag == 89)
2965  TempGraphic->Assign(RailGraphics->gl89set);
2966  else if (TempElement.SpeedTag == 90)
2967  TempGraphic->Assign(RailGraphics->gl90set);
2968  else if (TempElement.SpeedTag == 91)
2969  TempGraphic->Assign(RailGraphics->gl91set);
2970  else if (TempElement.SpeedTag == 92)
2971  TempGraphic->Assign(RailGraphics->gl92set);
2972  else if (TempElement.SpeedTag == 93)
2973  TempGraphic->Assign(RailGraphics->bm93set);
2974  else if (TempElement.SpeedTag == 94)
2975  TempGraphic->Assign(RailGraphics->bm94set);
2976  else if (TempElement.SpeedTag == 95)
2977  TempGraphic->Assign(RailGraphics->gl95set);
2978  TempGraphic->Transparent = true;
2979  TempGraphic->TransparentColor = Utilities->clTransparent;
2980  if (RouteType == TAllRoutes::AutoSigsRoute) {
2981  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2982  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2983  }
2984  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2985  }
2986  // new for version 0.6
2987  else if (TempElement.TrackType == SignalPost)
2988  {
2989  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2990  {
2991  for (int x = 0; x < 40; x++)
2992  {
2993  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2994  // need to stop aspect
2995  {
2996  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2997  break;
2998  }
2999  }
3000  }
3001  else // normal signal
3002  {
3003  TempGraphic->Assign(TempElement.GraphicPtr);
3004  // GraphicPtr set to normal signal in a signal track element
3005  }
3006  TempGraphic->Transparent = true;
3007  TempGraphic->TransparentColor = Utilities->clTransparent;
3008  if (RouteType == TAllRoutes::AutoSigsRoute) {
3009  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3010  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3011  }
3012  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3013  }
3014  else {
3015  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
3016  // can't name points gaps or signals so 'else' OK
3017  bool FoundFlag;
3018  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
3019  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
3020  if (FoundFlag)
3021  {
3022  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
3023  {
3024  GraphicPtr->Canvas->CopyRect(DestRect,
3025  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
3026  TempGraphic->Assign(RailGraphics->bmName);
3027  TempGraphic->Transparent = true;
3028  TempGraphic->TransparentColor = Utilities->clTransparent;
3029  if (RouteType == TAllRoutes::AutoSigsRoute)
3030  {
3031  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3032  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3033  }
3034  else
3035  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
3036  // draw track on top
3037  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3038  SourceRect);
3039  }
3040  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
3041  TempGraphic->Assign(TempElement.GraphicPtr);
3042  TempGraphic->Transparent = true;
3043  TempGraphic->TransparentColor = Utilities->clTransparent;
3044  // note that can't be an AutoSigsRoute
3045  // now overlay the LC central portion
3046  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
3047  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3048  SourceRect);
3049  }
3050  else {
3051  TempGraphic->Assign(TempElement.GraphicPtr);
3052  TempGraphic->Transparent = true;
3053  TempGraphic->TransparentColor = Utilities->clTransparent;
3054  if (RouteType == TAllRoutes::AutoSigsRoute) {
3055  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3056  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3057  }
3058  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3059  SourceRect);
3060  }
3061  }
3062  else {
3063  TempGraphic->Assign(TempElement.GraphicPtr);
3064  TempGraphic->Transparent = true;
3065  TempGraphic->TransparentColor = Utilities->clTransparent;
3066  if (RouteType == TAllRoutes::AutoSigsRoute) {
3067  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3068  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3069  }
3070  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3071  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
3072  }
3073  }
3074  delete TempGraphic;
3075  delete SourceGraphic;
3076  Utilities->CallLogPop();
3077  }
3078 */
3079 // ---------------------------------------------------------------------------
3080 
3081 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
3082 {
3083  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
3084  if(PlotElement[ArrayNumber] == -1)
3085  {
3086  Utilities->CallLogPop(676);
3087  return; // not plotted yet
3088  }
3089  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
3090  // set before plot so gap flashing stops first
3091  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3092  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
3093  // Only need to set ID for leading element, stays set until train finally leaves the element
3094  Plotted = true;
3095  Utilities->CallLogPop(677);
3096 }
3097 
3098 // ---------------------------------------------------------------------------
3099 
3100 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
3101 {
3102  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3103  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
3104 }
3105 
3106 // ---------------------------------------------------------------------------
3107 
3108 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
3109 {
3110  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
3111  HeadCode);
3112  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
3113  {
3114  Utilities->CallLogPop(678);
3115  return(true);
3116  }
3117  else
3118  {
3119  Utilities->CallLogPop(679);
3120  return(false);
3121  }
3122 }
3123 
3124 // ---------------------------------------------------------------------------
3125 
3126 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
3127 {
3128  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
3129  "," + HeadCode);
3130  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
3131  {
3132  Utilities->CallLogPop(680);
3133  return(true);
3134  }
3135  else
3136  {
3137  Utilities->CallLogPop(681);
3138  return(false);
3139  }
3140 }
3141 
3142 // ---------------------------------------------------------------------------
3143 
3144 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
3145 // test whether this train on a bridge on trackpos 0 & 1
3146 {
3147  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
3148  HeadCode);
3149  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
3150  {
3151  Utilities->CallLogPop(682);
3152  return(false);
3153  }
3154  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3155  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
3156  {
3157  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
3158  {
3159  throw Exception("Error, same train on two different bridge tracks");
3160  }
3161  else
3162  {
3163  Utilities->CallLogPop(684);
3164  return(true);
3165  }
3166  }
3167  else
3168  {
3169  Utilities->CallLogPop(685);
3170  return(false);
3171  }
3172 }
3173 
3174 // ---------------------------------------------------------------------------
3175 
3176 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3177 // test whether this train on a bridge on trackpos 2 & 3
3178 {
3179  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3180  HeadCode);
3181  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3182  {
3183  Utilities->CallLogPop(686);
3184  return(false);
3185  }
3186  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3187  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
3188  {
3189  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3190  Utilities->CallLogPop(687);
3191  return(true);
3192  }
3193  else
3194  {
3195  Utilities->CallLogPop(688);
3196  return(false);
3197  }
3198 }
3199 
3200 // ---------------------------------------------------------------------------
3201 
3202 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3203 {
3204  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3205  AnsiString(EntryPos) + "," + HeadCode);
3206  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3207 
3208  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3209  if(Track->GapFlashFlag)
3210  {
3212  {
3215  Track->GapFlashFlag = false;
3216  }
3217  }
3218  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3219  {
3220  if(EntryPos == -1)
3221  {
3222  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3223  }
3224  if(EntryPos < 2)
3225  {
3226  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
3227  }
3228  else
3229  {
3230  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
3231  }
3232  }
3233  Utilities->CallLogPop(690);
3234 }
3235 
3236 // ---------------------------------------------------------------------------
3237 
3238 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3239 {
3240  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3241  AnsiString(EntryPos) + "," + HeadCode);
3242  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3243  {
3244  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3245  }
3246  else
3247  {
3248  if(EntryPos == -1)
3249  {
3250  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3251  }
3252  if(EntryPos < 2)
3253  {
3254  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
3255  }
3256  else
3257  {
3258  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
3259  }
3260  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
3261  // i.e. other train on track 2&3
3262  {
3263  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
3264  }
3265  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
3266  // i.e. other train on track 1&2
3267  {
3268  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
3269  }
3270  else
3271  {
3272  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3273  }
3274  }
3275  Utilities->CallLogPop(691);
3276 }
3277 
3278 // ---------------------------------------------------------------------------
3279 
3280 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3281 {
3282  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3283  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3284  int LockedVectorNumber;
3285 
3286  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3287  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3288  {
3289  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3290  Utilities->CallLogPop(692);
3291  return;
3292  }
3293  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3294  // i.e other track is in a marked route
3295  // LinkPos doesn't have to be the entry position for the above check
3296  {
3297  TRect SourceRect, DestRect;
3298  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3299  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3300  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3301  // identify the route element for the other track
3302  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3303  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3304  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3305  int FirstELink, SecondELink = -1;
3306  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3307  // must be at least one
3308  if(RoutePair2.first > -1)
3309  {
3310  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3311  }
3312  TPrefDirElement RouteElement;
3313  // Graphics::TBitmap *RouteGraphic;
3314  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3315  // i.e. other track is in RoutePair2
3316  {
3317  if(SecondELink == -1)
3318  {
3319  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3320  }
3321  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3322  // error if both have same Link number
3323  {
3324  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3325  }
3326  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3327  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3328  }
3329  else // other track is in RoutePair1
3330  {
3331  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3332  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3333  }
3334  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3335  DestGraphic->PixelFormat = pf8bit;
3336  DestGraphic->Width = 8;
3337  DestGraphic->Height = 8;
3338  DestGraphic->Transparent = true;
3339  // has to be transparent or will overwrite the track that the train has just left
3340  DestGraphic->TransparentColor = Utilities->clTransparent;
3341  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3342  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3343  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3344  // plot locked route marker for other route if appropriate
3345  TPrefDirElement PrefDirElement; // holder for next call, unused
3346  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3347  if(StraddleValue == LeadMidLag)
3348  {
3350  PrefDirElement, LockedVectorNumber))
3351  {
3352  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3353  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3354  }
3355  }
3356  delete DestGraphic;
3357  }
3358  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3359  // also can only be a bridge or trains either have already or soon will crash
3360  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3361  {
3362  Utilities->CallLogPop(695);
3363  return;
3364  }
3365  if(ElementEntryPos > 1) // other train is on track 01
3366  {
3367  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3368  {
3370  }
3371  }
3372  else // other train is on track 23
3373  {
3374  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3375  {
3377  }
3378  }
3379  Utilities->CallLogPop(696);
3380 }
3381 
3382 // ---------------------------------------------------------------------------
3383 
3384 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3385 {
3386  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3387  AnsiString(EntryPos) + "," + HeadCode);
3388  int RouteNumber;
3389  bool WrongRoute = false;
3390  TPrefDirElement RouteElement;
3392  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3393 
3394  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3395  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3396  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3397  {
3398  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3399  {
3400  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3401  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3402  {
3403  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3404  {
3405  // don't call for stub end routes
3407  }
3408  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3409  Utilities->CallLogPop(697);
3410  return;
3411  }
3412  }
3413  // also need to check for a route on a crossing diagonal
3414  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3415  int LinkNumber = TrackElement.Link[EntryPos];
3416  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3417  {
3418  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3419  {
3420  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3421  bool LogActionErrorCalled = false;
3422  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3423  if(LinkNumber == 1)
3424  {
3425  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3426  {
3427  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3428  {
3429  // don't call for stub end routes
3431  LogActionErrorCalled = true;
3432  }
3433  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3434  }
3435  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3436  // not else in case have different routes on each diagonal, though shouldn't be possible
3437  {
3438  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3439  {
3440  // don't call for stub end routes
3442  }
3443  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3444  }
3445  }
3446 
3447  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3448  else if(LinkNumber == 3)
3449  {
3450  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3451  {
3452  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3453  {
3454  // don't call for stub end routes
3456  LogActionErrorCalled = true;
3457  }
3458  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3459  }
3460  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3461  // not else in case have different routes on each diagonal, though shouldn't be possible
3462  {
3463  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3464  {
3465  // don't call for stub end routes
3467  }
3468  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3469  }
3470  }
3471 
3472  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3473  else if(LinkNumber == 7)
3474  {
3475  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3476  {
3477  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3478  {
3479  // don't call for stub end routes
3481  LogActionErrorCalled = true;
3482  }
3483  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3484  }
3485  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3486  // not else in case have different routes on each diagonal, though shouldn't be possible
3487  {
3488  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3489  {
3490  // don't call for stub end routes
3492  }
3493  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3494  }
3495  }
3496 
3497  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3498  else if(LinkNumber == 9)
3499  {
3500  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3501  {
3502  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3503  {
3504  // don't call for stub end routes
3506  LogActionErrorCalled = true;
3507  }
3508  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3509  }
3510  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3511  // not else in case have different routes on each diagonal, though shouldn't be possible
3512  {
3513  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3514  {
3515  // don't call for stub end routes
3517  }
3518  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3519  }
3520  }
3521  }
3522  }
3523  Utilities->CallLogPop(698);
3524  return; // no route on other track or no other track
3525  }
3526  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3527  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3528  {
3529  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3530  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3531  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3532  {
3533  if(RouteElement.GetELinkPos() == EntryPos)
3534  {
3535  Utilities->CallLogPop(699);
3536  return; // right direction
3537  }
3538  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3539  {
3540  Utilities->CallLogPop(700);
3541  return; // right direction (points)
3542  }
3543  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3544  {
3545  Utilities->CallLogPop(701);
3546  return; // right direction (points)
3547  }
3548  else if(RouteElement.GetXLinkPos() == EntryPos)
3549  {
3550  WrongRoute = true;
3551  break; // wrong direction
3552  }
3553  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3554  {
3555  WrongRoute = true;
3556  break; // wrong direction
3557  }
3558  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3559  {
3560  WrongRoute = true;
3561  break; // wrong direction
3562  }
3563  }
3564  }
3565  if(!WrongRoute)
3566  {
3567  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3568  }
3569  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3570  {
3571  // don't call for stub end routes
3573  }
3574  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3575  Utilities->CallLogPop(703);
3576 }
3577 
3578 // ---------------------------------------------------------------------------
3579 
3580 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3581 {
3582  if(BackgroundColour == NewBackgroundColour)
3583  {
3584  return; // don't replot if already correct
3585 
3586  }
3587  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3588  bool ColourError = false, ColourError2 = false;
3589 
3590  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3591  if(ColourError)
3592  {
3593  ColourError2 = true;
3594  }
3595  for(int x = 0; x < 4; x++)
3596  {
3597  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3598  if(ColourError)
3599  {
3600  ColourError2 = true;
3601  }
3602  }
3603  if(ColourError2)
3604  {
3606  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3607  }
3608  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3609  // of motion
3610  for(int x = 0; x < 4; x++)
3611  {
3612  PlotTrainGraphic(6, x, Disp);
3613  }
3614  BackgroundColour = NewBackgroundColour;
3615  Display->Update();
3616  // need to keep this since Update() not called for PlotSmallOutput as too slow
3617  Utilities->CallLogPop(704);
3618 }
3619 
3620 // ---------------------------------------------------------------------------
3621 
3622 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3623 /*
3624 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3625 
3626 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3627 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3628 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3629 full-element moves.
3630 
3631 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3632 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3633 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3634 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3635 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3636 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3637 In this case set the brake rate to maximum to stop as soon as possible.
3638 
3639 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3640 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3641 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3642 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3643 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3644 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3645 
3646 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3647 first to see whether buffers or continuation) in turn is examined: first the length of the
3648 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3649 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3650 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3651 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3652 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3653 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3654 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3655 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3656 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3657 siding then again emeregency braking may be necessary and a crash may result.
3658 
3659 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3660 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3661 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3662 buffer, then the train accelerates for half the element and brakes for the other half.
3663 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3664 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3665 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3666 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3667 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3668 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3669 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3670 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3671 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3672 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3673 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3674 
3675 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3676 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3677 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3678 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3679 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3680 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3681 MaxBrakeRate.
3682 
3683 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3684 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3685 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3686 
3687 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3688 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3689 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3690 
3691 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3692 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3693 when Straddle == LeadMidLag
3694 */
3695 {
3696  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3697  AnsiString(EntryPos) + "," + HeadCode);
3698  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3699  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3700  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3701  TrainInFrontInSignallerModeFlag = false;
3702  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3703  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3704  bool SignallerStopRequired = false;
3705 
3707  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3708 
3709  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3710 
3711  OneLengthAccelDecel = false;
3712  BrakeRate = 0;
3713 
3714 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3715  if(CurrentTrackVectorPosition > -1)
3716  {
3717  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3718  {
3719  if((EntryPos == 0) || (EntryPos == 2))
3720  {
3721  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3722  {
3723  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3724  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3725  }
3726  else
3727  {
3728  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3729  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3730  }
3731  }
3732  else if(EntryPos == 1)
3733  {
3734  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3735  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3736  }
3737  else // == 3
3738  {
3739  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3740  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3741  }
3742  }
3743  else
3744  {
3745  if(EntryPos > 1)
3746  {
3747  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3748  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3749  }
3750  else
3751  {
3752  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3753  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3754  }
3755  }
3756  EntryHalfLength = CurrentElementHalfLength;
3757  FrontElementLength = 2 * CurrentElementHalfLength;
3758  }
3759  else
3760  {
3761  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3762  }
3763  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3764  {
3765  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3766  }
3767  // check if zero entry speed with another train directly in front & if so remain stopped
3768  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3769  {
3770  EntrySpeed = 0;
3771  ExitSpeedHalf = 0;
3772  ExitSpeedFull = 0;
3773  MaxExitSpeed = 0;
3774  BrakeRate = 0;
3775  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3776  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3777  StoppedForTrainInFront = true;
3778  Utilities->CallLogPop(705);
3779  return;
3780  }
3781  // new at v2.4.0 - check for stopped and zero power
3782  if((EntrySpeed < 1) && PowerAtRail < 1)
3783  {
3784  EntrySpeed = 0;
3785  ExitSpeedHalf = 0;
3786  ExitSpeedFull = 0;
3787  MaxExitSpeed = 0;
3788  BrakeRate = 0;
3789  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3790  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3791  StoppedWithoutPower = true;
3792  Utilities->CallLogPop(2125);
3793  return;
3794  }
3795 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3796  if(BeingCalledOn)
3797  {
3798  LimitingSpeed = CallOnMaxSpeed;
3799  }
3800  else
3801  {
3802  LimitingSpeed = MaximumSpeedLimit;
3803  }
3804  if(LimitingSpeed > FrontElementSpeedLimit)
3805  {
3806  LimitingSpeed = FrontElementSpeedLimit;
3807  }
3808  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3809  {
3810  LimitingSpeed = MaxRunningSpeed;
3811  }
3812  FrontElementMaxSpeed = LimitingSpeed;
3813 
3814 /*
3815  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3816  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3817  (2) V/3.6 = U/3.6 - FT;
3818  (3) S = UT/3.6 - 0.5FT^2
3819  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3820 
3821  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3822  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3823  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3824  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3825  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3826 
3827  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3828  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3829  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3830 */
3831 
3832 // check if running past a red signal without permission
3833  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3834  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3835  {
3836  SPADFlag = true; // user has to intervene to reset & restart after spad
3837  }
3838  if(!SPADFlag)
3839  {
3840  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3841  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3842  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3843  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3844  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3845  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3846 
3847  double ExitSpeedAtMaxBraking;
3848  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3849  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3851  {
3852  ExitSpeedAtMaxBraking = 0;
3853  }
3854  else
3855  {
3856  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3857  }
3858  double SpeedToUse;
3859  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3860  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3861  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3862  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3863  {
3864  SpeedToUse = ExitSpeedAtMaxBraking;
3865  }
3866  else
3867  {
3868  SpeedToUse = LimitingSpeed;
3869  }
3870  if(ExitSpeedFull > SpeedToUse)
3871  {
3872  ExitSpeedFull = SpeedToUse;
3873  }
3874  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3875  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3876 
3877  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3878  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3879  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3880 
3881  do
3882  {
3883  RedSignalFlag = false;
3884  BuffersFlag = false;
3885  StationFlag = false;
3886  BuffersOrContinuationNowFlag = false;
3887  ContinuationNextFlag = false;
3888  // have to reset this after the above test
3889  // add current element length to CumulativeLength
3890  CumulativeLength += (2 * CurrentElementHalfLength);
3891  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3892  {
3893  SignallerStopRequired = true;
3894  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3895  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3896  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3897  if(SignallerStopBrakeRate < TempBR)
3898  {
3899  SignallerStopBrakeRate = TempBR;
3900  }
3901  }
3902  // first check for stops within the length of the current element, where don't want any more checks & don't want
3903  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3904  // during the last loop when the NextTrackVectorPosition was the signal.
3905 
3906  // check if current element is a buffer
3907  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3908  {
3909  // no need to add in the length of this element to CumulativeLength as already included
3910  BuffersFlag = true;
3911  }
3912  // check if current element is a station stop
3913  if(TrainMode == Timetable)
3914  {
3915  bool StopRequired = false;
3916  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3917  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3918  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3919  {
3920  // no need to add in the length of element to CumulativeLength
3921  if(StopRequired)
3922  {
3923  StationFlag = true;
3924  }
3925  }
3926  }
3927  else
3928  {
3929  StationFlag = false;
3930  }
3931  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3932  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3933  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3934  {
3935  BuffersOrContinuationNowFlag = true;
3936  }
3937  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3938  {
3939  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3940  {
3941  if((EntryPos == 0) || (EntryPos == 2))
3942  {
3943  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3944  {
3945  ExitPos = 1;
3946  }
3947  else
3948  {
3949  ExitPos = 3;
3950  }
3951  }
3952  else
3953  {
3954  ExitPos = 0;
3955  }
3956  }
3957  else
3958  {
3959  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3960  }
3961  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3962  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3963  if(NextTrackVectorPosition > -1)
3964  {
3965  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3966  // this test & section added at v0.6
3967  {
3968  if((NextEntryPos == 0) || (NextEntryPos == 2))
3969  {
3970  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3971  {
3972  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3973  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3974  }
3975  else
3976  {
3977  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3978  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3979  }
3980  }
3981  else if(NextEntryPos == 1)
3982  {
3983  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3984  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3985  }
3986  else // == 3
3987  {
3988  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3989  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3990  }
3991  }
3992  else
3993  {
3994  if(NextEntryPos > 1)
3995  {
3996  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3997  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3998  }
3999  else
4000  {
4001  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
4002  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
4003  }
4004  }
4005  }
4006  else
4007  {
4008  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
4009  }
4010  // now check for stops, first cover those where don't want to add in length of next element
4011  // check if next element is a red signal - Attr 0,
4012  // note that this doesn't apply to trains stopped at a red signal since the signal position is
4013  // CurrentTrackVectorPosition not NextTrackVectorPosition
4014  bool StopRequired;
4015  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
4016  {
4017  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
4018  {
4019  // no need to add in the length of element to CumulativeLength
4020  RedSignalFlag = true;
4021  }
4022  // next element is a red signal
4023  }
4024  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
4025  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
4026  // at least one platform element free
4028  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
4029  NextEntryPos, TrainID))
4030  {
4031  // no need to add in the length of element to CumulativeLength
4032  if(StopRequired)
4033  {
4034  StationFlag = true;
4035  }
4036  }
4037  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
4038  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
4039  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
4040  {
4041  // no need to add in the length of element to CumulativeLength
4042  TrainInFrontInSignallerModeFlag = true;
4043  }
4044  // check if next element is a buffer
4045  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
4046  {
4047  // need to add in the length of that element to CumulativeLength
4048  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
4049  BuffersFlag = true;
4050  }
4051  // check if next element is a station stop
4053  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
4054  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
4055  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
4056  {
4057  // need to add in the length of that element to CumulativeLength if a stop required
4058  if(StopRequired)
4059  {
4060  StationFlag = true;
4061  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
4062  }
4063  }
4064  }
4065  //now can decide whether need to stop over CumulativeLength
4066  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
4067  {
4068  // have to come to a stop over CumulativeLength
4069  if(CumulativeLength == FrontElementLength)
4070  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
4071  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
4072  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
4073  // and if less than EntrySpeed then skip this section (don't need any acceleration)
4074  // if not calc speed at halfway point & if less than above set half speed to this value;
4075  // use constant acceleration in calculating half time point
4076  {
4077  MaxExitSpeed = 0;
4078  double MaxHalfSpeed;
4079  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
4080  // have to halve the element length, & can't be zero or negative so no need to test
4081  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4082  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
4083  {
4084  MaxHalfSpeed = FrontElementMaxSpeed;
4085  }
4086  else
4087  {
4088  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4089  }
4090  if(MaxHalfSpeed > (2 * EntrySpeed))
4091  // use 2x to prevent kangarooing at last element when had
4092  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
4093  {
4094  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4095  0.333334);
4096  bool HalfSpeedLimited = false;
4097  if(MaxHalfSpeed < ExitSpeedHalf)
4098  {
4099  ExitSpeedHalf = MaxHalfSpeed;
4100  HalfSpeedLimited = true;
4101  }
4102  if(PowerAtRail > 1)
4103  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4104  {
4105  // [km/h/3.6 = m/s]
4106  ExitTimeHalf =
4107  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4108  }
4109  else
4110  {
4111  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4112  }
4113  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
4114  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
4115  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
4116  // by a long braking period
4117  ExitSpeedFull = 0;
4118  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
4119  if(TempBrakeRate > MaxBrakeRate)
4120  {
4121  TempBrakeRate = MaxBrakeRate;
4122  }
4123  // shouldn't be but leave in anyway
4124  if(TempBrakeRate > BrakeRate)
4125  {
4126  BrakeRate = TempBrakeRate;
4127  }
4128  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4129  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
4130  if(HalfSpeedLimited)
4131  // this is the change referred to above
4132  {
4133  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
4134  ExitTimeHalf = EntryTime + BrakingTime;
4135  ExitTimeFull = ExitTimeHalf + BrakingTime;
4136  }
4137  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
4138  Utilities->CallLogPop(1095);
4139  return;
4140  }
4141  }
4142  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
4143  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
4144  // calc th, tf, sh, & sf
4145  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
4146  if(TempBrakeRate > MaxBrakeRate)
4147  {
4148  TempBrakeRate = MaxBrakeRate;
4149  }
4150  if(TempBrakeRate > BrakeRate)
4151  {
4152  BrakeRate = TempBrakeRate;
4153  }
4154  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4155  if(SignallerStopRequired)
4156  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4157  {
4159  {
4161  // this prevents the brakerate from reducing for a signaller stop
4162  // regardless of other conditions that may change as progress round the loop
4163  }
4164  }
4166  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4167  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4168  {
4170  }
4171  int TempMaxExitSpeed;
4172  // calc current value & if less than MaxExitSpeed set that to this
4173  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4174  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4175  {
4176  MaxExitSpeedAtHalfBraking = 0;
4177  }
4178  else
4179  {
4180  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4181  }
4182  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4183  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4184  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4185  {
4186  TempMaxExitSpeed = FrontElementMaxSpeed;
4187  }
4188  else
4189  {
4190  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4191  }
4192  if(TempMaxExitSpeed < MaxExitSpeed)
4193  {
4194  MaxExitSpeed = TempMaxExitSpeed;
4195  }
4196  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4197  // Cumulativelength, and Cumulativelength
4198 
4199  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4200  {
4201  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4202  if(ExitSpeedHalfSquared < 10)
4203  {
4204  ExitSpeedHalf = 0;
4205  }
4206  else
4207  {
4208  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4209  }
4210  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4211  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4212  if(ExitSpeedFullSquared < 10)
4213  {
4214  ExitSpeedFull = 0;
4215  }
4216  else
4217  {
4218  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4219  }
4220  if((StationFlag) && (CumulativeLength == FrontElementLength))
4221  {
4222  ExitSpeedFull = 0;
4223  // force a stop for station (not for buffers or red signal)
4224  }
4225  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4226  }
4227  // new condition at v2.4.0
4228  else if(PowerAtRail <= 1)
4229  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4230  // avoid using AValue in denominator or have excessively long times
4231  {
4232  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4233  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4234  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4235 
4236  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4237  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4238  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4239  }
4240  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4241  // without the power need above condition or have hours of delay times, above added at v2.4.0
4242  {
4243  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4244  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4245  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4246  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4247  BrakeRate = 0;
4248  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4249  0.333334);
4250  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4251  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4253  // can accelerate continually over the half length
4254  {
4255  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4256  / 86400.0);
4258  // can accelerate continually over the full length
4259  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4260  {
4261  ExitTimeFull =
4262  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4263  }
4264  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4265  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4266  {
4267  // added at v0.6 as a safeguard
4268  if(MaxExitSpeed < EntrySpeed)
4269  {
4271  }
4272  // to prevent DeltaExitTimeToMaxInSecs being negative
4273  if(MaxExitSpeed < 1)
4274  {
4275  MaxExitSpeed = 1;
4276  }
4277  // to prevent divide by zero error
4278  // below as was
4280  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4281  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4282  (1.5 * AValue * AValue);
4283  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4284  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4285  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4286  }
4287  }
4288  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4289  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4290  // second halves of the element
4291  {
4292  // added at v0.6 as a safeguard
4293  if(MaxExitSpeed < EntrySpeed)
4294  {
4296  }
4297  // to prevent DeltaExitTimeToMaxInSecs being negative
4298  if(MaxExitSpeed < 1)
4299  {
4300  MaxExitSpeed = 1; // to prevent divide by zero error
4301  }
4302  // below as was
4304  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4305  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4306  (1.5 * AValue * AValue);
4307  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4308  // remaining distance to half length
4309  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4310  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4312  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4313  }
4314  }
4315  Utilities->CallLogPop(706);
4316  return;
4317  }
4318  else
4319  {
4320  if(!BuffersOrContinuationNowFlag)
4321  {
4322  if(NextSpeedLimit < LimitingSpeed)
4323  {
4324  LimitingSpeed = NextSpeedLimit;
4325  }
4326  }
4327  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4328  int TempMaxExitSpeed;
4329  // calc current value & if less than MaxExitSpeed set that to this
4330  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4331  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4332  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4333  {
4334  MaxExitSpeedAtHalfBraking = 0;
4335  }
4336  else
4337  {
4338  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4339  }
4340  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4341  {
4342  TempMaxExitSpeed = FrontElementMaxSpeed;
4343  }
4344  else
4345  {
4346  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4347  }
4348  if(TempMaxExitSpeed < MaxExitSpeed)
4349  {
4350  MaxExitSpeed = TempMaxExitSpeed;
4351  }
4352  // MaxExitSpeed is an external variable & this can reduce its value
4353  if(EntrySpeed > LimitingSpeed)
4354  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4355  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4356  {
4357  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4358  if(TempBrakeRate > MaxBrakeRate)
4359  {
4360  TempBrakeRate = MaxBrakeRate;
4361  }
4362  // shouldn't be for speedlimits since all known in advance, but include anyway
4363  if(TempBrakeRate > BrakeRate)
4364  {
4365  BrakeRate = TempBrakeRate;
4366  }
4367  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4368  }
4369  }
4370  if(!BuffersOrContinuationNowFlag)
4371  {
4372  CurrentTrackVectorPosition = NextTrackVectorPosition;
4373  EntryPos = NextEntryPos;
4374  CurrentElementHalfLength = NextElementHalfLength;
4375  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4376  {
4377  ContinuationNextFlag = true;
4378  }
4379  }
4380  }
4381  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4383  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4384  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4385  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4386  // stopping distance after it.
4387 
4388  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4389  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4390 
4391  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4392  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4393  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4394  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4395  // too late
4396 
4397  // set final braking or acc'n speed & time values
4398  if(BrakeRate > 0.01)
4399  {
4400  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4401  if(ExitSpeedHalfSquared < 10)
4402  {
4403  ExitSpeedHalf = 0;
4404  }
4405  else
4406  {
4407  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4408  }
4409  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4410  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4411  if(ExitSpeedFullSquared < 10)
4412  {
4413  ExitSpeedFull = 0;
4414  }
4415  else
4416  {
4417  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4418  }
4419  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4420  }
4421  else
4422  {
4423  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4424  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4425  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4426  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4427 
4428  BrakeRate = 0;
4429  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4430  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4431  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4433  {
4434  if(PowerAtRail > 1)
4435  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4436  {
4437  // [km/h/3.6 = m/s]
4438  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4439  / 86400.0);
4440  }
4441  else
4442  {
4443  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4444  }
4446  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4447  {
4448  if(PowerAtRail > 1)
4449  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4450  {
4451  // [km/h/3.6 = m/s]
4452  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4453  / 86400.0);
4454  }
4455  else
4456  {
4457  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4458  }
4459  }
4460  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4461  {
4462  // added at v0.6 as a safeguard
4463  if(MaxExitSpeed < EntrySpeed)
4464  {
4466  }
4467  // to prevent DeltaExitTimeToMaxInSecs being negative
4468  if(MaxExitSpeed < 1)
4469  {
4470  MaxExitSpeed = 1; // to prevent divide by zero error
4471  }
4472  // below as was
4474  double DeltaExitTimeToMaxInSecs;
4475  double DistanceToMax;
4476  if(PowerAtRail > 1) // added at v2.4.0
4477  {
4478  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4479  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4480  (1.5 * AValue * AValue);
4481  }
4482  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4483  {
4484  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4485  // these not really accurate but will be good enough
4486  DistanceToMax = EntryHalfLength;
4487  }
4488  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4489  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4490  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4491  }
4492  }
4493  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4494  {
4495  // added at v0.6 as a safeguard
4496  if(MaxExitSpeed < EntrySpeed)
4497  {
4499  }
4500  // to prevent DeltaExitTimeToMaxInSecs being negative
4501  if(MaxExitSpeed < 1)
4502  {
4503  MaxExitSpeed = 1; // to prevent divide by zero error
4504  }
4505  // below as was
4507  double DeltaExitTimeToMaxInSecs;
4508  double DistanceToMax;
4509  if(PowerAtRail > 1) // added at v2.4.0
4510  {
4511  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4512  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4513  (1.5 * AValue * AValue);
4514  }
4515  else
4516  {
4517  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4518  // these not really accurate but will be good enough
4519  DistanceToMax = EntryHalfLength / 2;
4520  }
4521  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4522  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4523  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4525  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4526  }
4527  }
4528  }
4529 
4530  else // SPADFlag set
4531  {
4533  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4534  if(ExitSpeedHalfSquared < 10)
4535  {
4536  ExitSpeedHalf = 0;
4537  }
4538  else
4539  {
4540  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4541  }
4542  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4543  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4544  if(ExitSpeedFullSquared < 10)
4545  {
4546  ExitSpeedFull = 0;
4547  }
4548  else
4549  {
4550  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4551  }
4552  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4553 
4554  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4555  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4556  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4557  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4558  // will be the stopping speed.
4559  if(ExitSpeedFull > 0)
4560  {
4561  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4562  {
4563  if((EntryPos == 0) || (EntryPos == 2))
4564  {
4565  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4566  {
4567  ExitPos = 1;
4568  }
4569  else
4570  {
4571  ExitPos = 3;
4572  }
4573  }
4574  else
4575  {
4576  ExitPos = 0;
4577  }
4578  }
4579  else
4580  {
4581  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4582  }
4583  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4584  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4585  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4586  {
4587  int NextElementLength;
4588  if(NextEntryPos > 1)
4589  {
4590  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4591  }
4592  else
4593  {
4594  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4595  }
4596  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4597  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4598  {
4599  ExitSpeedFull = 0;
4600  }
4601  }
4602  }
4603  }
4604  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4605  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4606  {
4607  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4608  if(LeadElement > -1)
4609  {
4611  // don't stop on a continuation either entering or leaving
4612  {
4615  MaxExitSpeed = LimitingSpeed;
4616  BrakeRate = 0;
4617  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4618  // assume length is 50m for ease of calc
4619  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4620  Utilities->CallLogPop(2126);
4621  return;
4622  }
4623  }
4624  else if(MidElement > -1)
4625  {
4627  {
4630  MaxExitSpeed = LimitingSpeed;
4631  BrakeRate = 0;
4632  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4633  // assume length is 50m for ease of calc
4634  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4635  Utilities->CallLogPop(2127);
4636  return;
4637  }
4638  }
4639  else if(LagElement > -1)
4640  {
4642  {
4645  MaxExitSpeed = LimitingSpeed;
4646  BrakeRate = 0;
4647  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4648  // assume length is 50m for ease of calc
4649  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4650  Utilities->CallLogPop(2128);
4651  return;
4652  }
4653  }
4654  if(EntrySpeed > 7.5) // keep going for at least another element
4655  {
4656  ExitSpeedHalf = EntrySpeed - 2.5;
4657  ExitSpeedFull = EntrySpeed - 5;
4658  MaxExitSpeed = LimitingSpeed;
4659  BrakeRate = 0;
4660  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4661  // assume length is 50m for ease of calc
4662  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4663  Utilities->CallLogPop(2129);
4664  return;
4665  }
4666  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4667  {
4668  // will appear to have slowed at steady rate
4669  ExitSpeedHalf = 0;
4670  ExitSpeedFull = 0;
4671  MaxExitSpeed = LimitingSpeed;
4672  BrakeRate = 0;
4673  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4674  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4675  StoppedWithoutPower = true;
4676  Utilities->CallLogPop(2130);
4677  return;
4678  }
4679  }
4680  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4681  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4682  Utilities->CallLogPop(707);
4683 }
4684 // ---------------------------------------------------------------------------
4685 /*
4686  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4687  {
4688  int NextExitPos;
4689  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4690  if(TimetableVector.empty()) return false;
4691  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4692  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4693  {
4694  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4695  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4696  {
4697  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4698  }
4699  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4700  {
4701  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4702  }
4703  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4704  NextElement = TempElement;
4705  }
4706  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4707  if(NextElement.TrackType == Buffers) return true;
4708  return false;//shouldn't reach here but include to prevent compiler return warning
4709  }
4710 */
4711 // ---------------------------------------------------------------------------
4712 
4713 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4714 /*
4715  returns the number by which the train ActionVectorEntryPtr needs
4716  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4717  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4718  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4719  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4720  or pass (false) the location.
4721 */{
4722  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4723  Stop = false;
4724  if(TimetableFinished || (Name == ""))
4725  {
4726  Utilities->CallLogPop(957);
4727  return(-1);
4728  }
4730 /*added the following check at v2.11.1 because of a fault found in Kevin Smith's railway (notified 02/01/22). CH01 started at
4731 Chester behind the stop position, but when it departed and this function was called it found Chester again with no cdt
4732 before it (it went round the Liverpool Loop), so it stopped again when it reached the stop position and reported all intermediate stops as having been
4733 missed. This check is for the train just having departed from the station in question, and if it has then any further stations
4734 with the same name are ignored - i.e. it stops a train from stopping at the same station twice in succession.
4735 */
4736  if(Ptr > &TrainDataEntryPtr->ActionVector.at(0))
4737  {
4738  Ptr--;
4739  if((Ptr->DepartureTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4740  {
4741  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4742  {
4743  Utilities->CallLogPop(2444);
4744  return(-1);
4745  }
4746  }
4747  }
4748  // start looking from current pointer position
4749  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4750  {
4751  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4752  {
4753  break;
4754  }
4755  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4756  {
4757  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4758  {
4759  Stop = true;
4760  Utilities->CallLogPop(960);
4761  return (Ptr - ActionVectorEntryPtr);
4762  }
4763  }
4764  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4765  {
4766  Utilities->CallLogPop(1517);
4767  return (Ptr - ActionVectorEntryPtr);
4768  }
4769  }
4770  Utilities->CallLogPop(959);
4771  return(-1); // not found a valid entry
4772 }
4773 
4774 // ---------------------------------------------------------------------------
4775 
4777 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4778  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4779  Ignores the call-on signal.
4780 */{
4781  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4782  int ReturnVal = 0;
4783  int ElementCount = 0;
4784 /* dropped at v2.12.0 as takes up a great deal of time unnecessarily - substitute 1000 elements instead and return true (very unlikley to need to search this far [10km at min length])
4785  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4786  {
4787  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4788  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4789  }
4790 */
4791  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4792  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4793 
4794  while(true)
4795  {
4796  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4797  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4798  {
4799  ReturnVal = 1;
4800  break;
4801  }
4802  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4803  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4804  {
4805  ReturnVal = 2;
4806  break;
4807  }
4808  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4809  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4810  {
4811  ReturnVal = 3;
4812  break;
4813  }
4814 /* not needed at and after v2.12.0, see above
4815  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388, CurrentTrackVectorPosition).TrackType == Crossover))
4816  {
4817  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4818  // must be a loop - reached same point as examined earlier
4819  {
4820  ReturnVal = 4;
4821  break;
4822  }
4823  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4824  {
4825  ReturnVal = 4;
4826  break;
4827  }
4828  }
4829  else
4830  {
4831  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526, CurrentTrackVectorPosition).TempTrackMarker23))
4832  {
4833  ReturnVal = 4;
4834  break;
4835  }
4836  }
4837  if(EntryPos < 2)
4838  {
4839  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4840  }
4841  else
4842  {
4843  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4844  }
4845 */
4846 
4847  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4848  {
4849  if((EntryPos == 0) || (EntryPos == 2))
4850  {
4851  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4852  {
4853  ExitPos = 1;
4854  }
4855  else
4856  {
4857  ExitPos = 3;
4858  }
4859  }
4860  else
4861  {
4862  ExitPos = 0;
4863  }
4864  }
4865  else
4866  {
4867  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4868  }
4869  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4870  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4871  CurrentTrackVectorPosition = NextTrackVectorPosition;
4872  EntryPos = NextEntryPos;
4873  ElementCount++;
4874  if(ElementCount > 1000)
4875  {
4876  ReturnVal = 4;
4877  break;
4878  }
4879  }
4880  if(ReturnVal == 1)
4881  {
4882  Utilities->CallLogPop(708);
4883  return(false);
4884  }
4885  if(ReturnVal == 2)
4886  {
4887  Utilities->CallLogPop(709);
4888  return(true);
4889  }
4890  if(ReturnVal == 3)
4891  {
4892  Utilities->CallLogPop(946);
4893  return(true);
4894  }
4895  if(ReturnVal == 4)
4896  {
4897  Utilities->CallLogPop(947);
4898  return(true);
4899  }
4900  else
4901  {
4902  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4903  }
4904 }
4905 
4906 // ---------------------------------------------------------------------------
4907 
4909 /*
4910  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4911  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4912  change of direction (cdt), remaining here (Frh), or under signaller control);
4913  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4914  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4915  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4916  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
4917  m) not failed or stopped without power
4918  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4919  change points outside the route or have a route conflict if another route is set.
4920 */{
4921  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
4922  {
4923  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
4924  }
4925  // some of the callingon route elements may be involved
4926  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4927  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4928  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4929  int RouteStartPosition;
4930  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4931  int PlatformPosition;
4932  // the track vector position of the first stop platfrom
4933  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4934  // not used here
4935  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4936  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4937  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4938  // must be stopped at a signal but not at a location & still in timetable (a)
4940  // no need to check for SignallerStopped as this function only called in Timetable mode
4941  {
4942  Utilities->CallLogPop(711);
4943  return(false);
4944  }
4945  while(true)
4946  {
4947  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4948  // don't look further than 4km ahead (j)
4949  if(Distance > (4000 + LeadElementDistance))
4950  {
4951  Utilities->CallLogPop(967);
4952  return(false);
4953  }
4954  // if find another train on an element in front, before find a valid platform, return false (c)
4955  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4956  {
4957  Utilities->CallLogPop(713);
4958  return(false);
4959  }
4960  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4961  // be facing later on)
4962  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4963  {
4964  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4965  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4966  if(OtherTrain.LeadElement == -1)
4967  {
4968  Utilities->CallLogPop(714);
4969  return(false);
4970  }
4971  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4972  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4973  {
4974  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4975  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4976  (OtherTrain.TrainMode == Signaller))
4977  {
4978  break;
4979  }
4980  else
4981  {
4982  Utilities->CallLogPop(955);
4983  return(false);
4984  }
4985  }
4986  else // (h)
4987  {
4988  break;
4989  }
4990  }
4991  // if reach buffers or exit continuation return false (can set route)
4992  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4993  {
4994  Utilities->CallLogPop(716);
4995  return(false);
4996  }
4997  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4998  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
5000  {
5001  Utilities->CallLogPop(717);
5002  return(false);
5003  }
5004  // if reach a location that isn't in timetable return false - drop this as still can't set a route
5005 /*
5006  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
5007  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
5008  {
5009  Utilities->CallLogPop(718);
5010  return false;
5011  }
5012 */
5013  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
5014  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
5015  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
5016  {
5017  if(StopRequired)
5018  {
5019  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
5020  {
5021  if(!PlatformFoundFlag)
5022  {
5023  PlatformPosition = CurrentTrackVectorPosition;
5024  }
5025  // ensure this only set once at first valid platform position - the unrestricted route will end here
5026  PlatformFoundFlag = true;
5027  }
5028  }
5029  }
5030  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
5031  // train has to be at station but that has to be before the next forward signal
5032 /*
5033  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
5034  {
5035  Utilities->CallLogPop(719);
5036  return false;
5037  }
5038 */
5039  // make sure points are followed correctly (d) & set ExitPos
5040  if(CurrentTrackElement.TrackType == Points)
5041  {
5042  if((EntryPos == 0) || (EntryPos == 2))
5043  {
5044  if(CurrentTrackElement.Attribute == 0)
5045  {
5046  ExitPos = 1;
5047  }
5048  else
5049  {
5050  ExitPos = 3;
5051  }
5052  }
5053  if(EntryPos == 1)
5054  {
5055  if(CurrentTrackElement.Attribute == 0)
5056  {
5057  ExitPos = 0;
5058  }
5059  else
5060  {
5061  Utilities->CallLogPop(720);
5062  return(false);
5063  }
5064  }
5065  if(EntryPos == 3)
5066  {
5067  if(CurrentTrackElement.Attribute == 1)
5068  {
5069  ExitPos = 0;
5070  }
5071  else
5072  {
5073  Utilities->CallLogPop(721);
5074  return(false);
5075  }
5076  }
5077  }
5078  else
5079  {
5080  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
5081  }
5082  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
5083  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
5084  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
5085  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
5086  if(ElementNumber < 2)
5087  {
5088  SkipRouteCheck = true;
5089  }
5090  else
5091  {
5092  SkipRouteCheck = false;
5093  }
5094  if(ElementNumber == 1) // the stop signal
5095  {
5096  RouteStartPosition = CurrentTrackVectorPosition;
5097  }
5098 /*
5099  if(ElementNumber == 2)
5100  {
5101  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
5102  else AutoSigs = false;
5103  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
5104  }
5105 */
5106  if(ElementNumber > 1)
5107  {
5108  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
5109  {
5110  RouteOrPartRouteSet = true;
5111  }
5112  else
5113  {
5114  RouteOrPartRouteSet = false;
5115  }
5116  }
5117  if(!SkipRouteCheck && !RouteOrPartRouteSet)
5118  {
5119  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
5120  {
5121  Utilities->CallLogPop(1859);
5122  return(false);
5123  }
5124  int ExitLink = CurrentTrackElement.Link[ExitPos];
5125  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
5126  {
5127  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
5128  {
5129  Utilities->CallLogPop(1850);
5130  return(false);
5131  }
5132  }
5133  }
5134  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
5135  if(EntryPos < 2)
5136  {
5137  Distance += CurrentTrackElement.Length01;
5138  }
5139  else
5140  {
5141  Distance += CurrentTrackElement.Length23;
5142  }
5143  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
5144  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
5145  CurrentTrackVectorPosition = NextTrackVectorPosition;
5146  EntryPos = NextEntryPos;
5147  ElementNumber++;
5148  } // while(true)
5149 
5150  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
5151  // from the stop signal (note that it may be last in an autosigs route)
5152  // a single element route at the stop signal should have been removed prior to this function being called (that called before
5153  // this in ClockTimer2)
5154 
5155  // now add elements to the CallonVector
5156  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
5157 
5158  AllRoutes->CallonVector.push_back(CallonEntry);
5159  Utilities->CallLogPop(1860);
5160  return(true); // return false if fail to set route for any reason
5161 }
5162 
5163 // ---------------------------------------------------------------------------
5164 /*
5165  bool TTrain::TimetableFinished()
5166  {
5167  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
5168  {
5169  return true;
5170  }
5171  return false;
5172  }
5173 */
5174 // ---------------------------------------------------------------------------
5175 
5176 AnsiString TTrain::GetTrainHeadCode(int Caller)
5177 
5178 {
5179  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5180  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5181 
5182  Utilities->CallLogPop(1452);
5183  return(RepeatHeadCode);
5184 }
5185 
5186 // ---------------------------------------------------------------------------
5187 
5188 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5189 {
5190  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5191  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5192 
5193  Utilities->CallLogPop(1453);
5194  return(RepeatTime);
5195 }
5196 
5197 // ---------------------------------------------------------------------------
5198 
5199 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5200 {
5201  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5202  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5203  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5204  // first check that train is fully on the railway
5205  bool FrontValid = false, RearValid = false;
5206  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5207 
5208  if((LeadElement == -1) || (MidElement == -1))
5209  {
5210  TrainToBeJoinedBy = NULL;
5211  Utilities->CallLogPop(2131);
5212  return(false);
5213  }
5215  {
5216  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5217  FrontValid = true;
5218  }
5220  {
5221  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5222  RearValid = true;
5223  }
5224  int TrainToBeJoinedByID = -1;
5225 
5226  // first check if on a 2-track element & select correct ID number if so
5227  if(FrontValid)
5228  {
5229  if(FrontAdjacentTrackElement.TrackType == Bridge)
5230  {
5232  {
5233  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5234  }
5235  else
5236  {
5237  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5238  }
5239  }
5240  else
5241  {
5242  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5243  }
5244  }
5245  if((TrainToBeJoinedByID < 0) && RearValid)
5246  {
5247  // first check if on a 2-track element & select correct ID number if so
5248  if(RearAdjacentTrackElement.TrackType == Bridge)
5249  {
5251  {
5252  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5253  }
5254  else
5255  {
5256  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5257  }
5258  }
5259  else
5260  {
5261  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5262  }
5263  }
5264  if(TrainToBeJoinedByID < 0) // no adjacent train
5265  {
5266  TrainToBeJoinedBy = NULL;
5267  Utilities->CallLogPop(2132);
5268  return(false);
5269  }
5270  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5271  if(!TrainToBeJoinedBy->Stopped())
5272  {
5273  TrainToBeJoinedBy = NULL;
5274  Utilities->CallLogPop(2133);
5275  return(false);
5276  }
5277  Utilities->CallLogPop(2134);
5278  return(true);
5279 }
5280 
5281 // ---------------------------------------------------------------------------
5282 
5283 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5284  TDateTime TimetableNonRepeatTime, bool Warning)
5285 /*
5286  Time = timetable time, the time adjustments for repeat trains is carried out internally
5287  Not all messages need this, if not needed a dummy value is required but not used
5288 
5289  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5290  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5291  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5292  //NB for Frh just give terminated message but without event time - don't use this function
5293  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5294  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5295  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5296  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5297  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5298  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5299  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5300  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5301  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5302  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5303  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5304  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5305  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5306  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5307  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5308  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5309  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
5310  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5311  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5312  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5313  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5314  SignallerStop 06:05:40: 2F46 stopped on signaller command
5315  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5316  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5317 */{
5318  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5319  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5320  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5321  int IntMinsLate = 0;
5322 
5323  // need to set it in case MinsLate == 0, since it isn't tested for that
5324  if(ActionType == Arrive)
5325  {
5326  ActionLog = " arrived at ";
5327  }
5328  if(ActionType == Terminate)
5329  {
5330  if(TerminatedMessageSent) // to avoid it being sent twice
5331  {
5332  Utilities->CallLogPop(1104);
5333  return;
5334  }
5335  ActionLog = " terminated at ";
5336  TerminatedMessageSent = true;
5337  }
5338  if(ActionType == Depart)
5339  {
5340  ActionLog = " departed from ";
5341  }
5342  if(ActionType == Pass)
5343  {
5344  ActionLog = " passed ";
5345  }
5346  if(ActionType == Create)
5347  {
5348  ActionLog = " created at ";
5349  }
5350  if(ActionType == Enter)
5351  {
5352  ActionLog = " entered railway at ";
5353  }
5354  if(ActionType == Leave)
5355  {
5356  ActionLog = " left railway at ";
5357  }
5358  if(ActionType == FrontSplit)
5359  {
5360  ActionLog = " split from front to ";
5361  }
5362  if(ActionType == RearSplit)
5363  {
5364  ActionLog = " split from rear to ";
5365  }
5366  if(ActionType == JoinedByOther)
5367  {
5368  ActionLog = " joined by ";
5369  }
5370  if(ActionType == ChangeDirection)
5371  {
5372  ActionLog = " changed direction at ";
5373  }
5374  if(ActionType == NewService)
5375  {
5376  ActionLog = " became new service ";
5377  }
5378  if(ActionType == TakeSignallerControl)
5379  {
5380  ActionLog = " taken under signaller control at ";
5381  }
5382  if(ActionType == RestoreTimetableControl)
5383  {
5384  ActionLog = " restored to timetable control at ";
5385  }
5386  if(ActionType == RemoveTrain)
5387  {
5388  if(Crashed)
5389  {
5390  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5391  }
5392  else if(Derailed)
5393  {
5394  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5395  }
5396  else
5397  {
5398  ActionLog = " REMOVED FROM RAILWAY at ";
5399  }
5400  }
5401  if(ActionType == SignallerMoveForwards)
5402  {
5403  ActionLog = " received signaller authority to proceed";
5404  }
5405  if(ActionType == SignallerStepForward)
5406  {
5407  ActionLog = " received signaller authority to step forward";
5408  }
5409  if(ActionType == SignallerChangeDirection)
5410  {
5411  ActionLog = " changed direction under signaller control at ";
5412  }
5413  if(ActionType == SignallerPassRedSignal)
5414  {
5415  ActionLog = " received signaller authority to pass red signal";
5416  }
5417  if(ActionType == SignallerControlStop)
5418  {
5419  ActionLog = " received signaller instruction to stop";
5420  }
5421  if(ActionType == SignallerStop)
5422  {
5423  ActionLog = " stopped on signaller instruction ";
5424  }
5425  if(ActionType == SignallerJoin)
5426  {
5427  ActionLog = " joined under signaller control by ";
5428  }
5429  if(ActionType == TrainFailure)
5430  {
5431  ActionLog = " suffered an onboard power failure at ";
5432  }
5433  if(ActionType == RepairFailedTrain)
5434  {
5435  ActionLog = " failure repaired at ";
5436  }
5437  if(ActionType == SignallerLeave)
5438  {
5439  ActionLog = " left railway under signaller control at ";
5440  }
5441  if(OtherHeadCode != "")
5442  {
5443  OtherHeadCode += " at ";
5444  }
5445  TDateTime ActualTime = TrainController->TTClockTime;
5446 
5447  if(Warning)
5448  {
5449  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5450  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
5451  }
5452  else
5453  {
5454  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5455  }
5456  bool TimePerformance = true;
5457 
5458  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5459  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5460  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5461  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5462  // SignallerJoin & RepairFailedTrain new at v2.4.0
5463  {
5464  TimePerformance = false;
5465  }
5466  if(TimePerformance)
5467  {
5468  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5469  MinsDelayed = float(MinsLate);
5470  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5471  {
5472  MinsDelayed = 0;
5473  }
5474  // new v2.2.0 for OpActionPanel, can be positive or negative
5475  if(ActionType == Arrive)
5476  {
5478  }
5479  // since train has just arrived this value is the
5480  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5481  // subtracted from later stop recoverable times.
5482  if(MinsLate < 0)
5483  {
5484  IntMinsLate = int(ceil(MinsLate));
5485  }
5486  if(MinsLate > 0)
5487  {
5488  IntMinsLate = int(floor(MinsLate));
5489  }
5490  if(IntMinsLate == 0)
5491  {
5492  PerfLog = " on time";
5493  }
5494  else if(IntMinsLate == 1)
5495  {
5496  PerfLog = " 1 minute late";
5497  }
5498  else if(IntMinsLate == -1)
5499  {
5500  PerfLog = " 1 minute early";
5501  }
5502  else if(IntMinsLate > 1)
5503  {
5504  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5505  }
5506  else if(IntMinsLate < -1)
5507  {
5508  int PosIntMinsLate = -IntMinsLate;
5509  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5510  }
5511  if(LocationName.Pos('-') > 0)
5512  {
5513  PerfLog = "," + PerfLog;
5514  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5515  }
5516  Display->PerformanceLog(0, BaseLog + PerfLog);
5517  }
5518  else
5519  {
5520  Display->PerformanceLog(1, BaseLog);
5521  }
5522  if(Warning)
5523  {
5524  Display->WarningLog(0, WarningBaseLog);
5525  }
5526  // update statistics
5527  if((ActionType == Arrive) && (IntMinsLate == 0))
5528  {
5530  }
5531  else if((ActionType == Arrive) && (IntMinsLate > 0))
5532  {
5534  TrainController->TotLateArrMins += IntMinsLate;
5535  }
5536  else if((ActionType == Arrive) && (IntMinsLate < 0))
5537  {
5539  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5540  }
5541 
5542  else if((ActionType == Pass) && (IntMinsLate == 0))
5543  {
5545  }
5546  else if((ActionType == Pass) && (IntMinsLate > 0))
5547  {
5549  TrainController->TotLatePassMins += IntMinsLate;
5550  }
5551  else if((ActionType == Pass) && (IntMinsLate < 0))
5552  {
5554  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5555  }
5556 
5557  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5558  {
5560  }
5561  else if((ActionType == Leave) && (IntMinsLate > 0))
5562  {
5564  TrainController->TotLateExitMins += IntMinsLate;
5565  }
5566  else if((ActionType == Leave) && (IntMinsLate < 0))
5567  {
5569  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5570  }
5571 
5572  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5573  {
5575  }
5576  else if((ActionType == Depart) && (IntMinsLate > 0))
5577  {
5579  TrainController->TotLateDepMins += IntMinsLate;
5580  }
5581  Utilities->CallLogPop(968);
5582 }
5583 
5584 // ---------------------------------------------------------------------------
5585 
5586 void TTrain::TrainHasFailed(int Caller)
5587 {
5588  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5589  if(Crashed || Derailed || DerailPending)
5590  {
5591  TrainFailurePending = false;
5592  Utilities->CallLogPop(2135);
5593  return;
5594  }
5595  AnsiString LocName = "";
5596 
5597  if(LeadElement > -1)
5598  {
5600  }
5601  if((LocName == "") && (MidElement > -1))
5602  {
5604  }
5605  if((LocName == "") && LeadElement > -1)
5606  {
5607  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5608  }
5609  if((LocName == "") && (MidElement > -1))
5610  {
5611  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5612  }
5613  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5614  TrainFailed = true;
5615  TrainFailurePending = false;
5616  CallingOnFlag = false; //added at v2.10.0
5618  PowerAtRail = 0.08;
5619  AValue = sqrt(2 * PowerAtRail / Mass);
5621  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5622  if(Stopped())
5623  {
5624  EntrySpeed = 0;
5625  ExitSpeedHalf = 0;
5626  ExitSpeedFull = 0;
5627  MaxExitSpeed = 0;
5628  BrakeRate = 0;
5629  StoppedWithoutPower = true;
5630  }
5632  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5633  // true for warning, TDateTime not used
5634  Utilities->CallLogPop(2136);
5635 }
5636 
5637 // ---------------------------------------------------------------------------
5638 
5639 void TTrain::FrontTrainSplit(int Caller)
5640 {
5641 /*
5642  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5643  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5644  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5645  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5646 */
5647  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5648  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5649  if(PowerAtRail < 1)
5650  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5651  {
5653  {
5654  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5655  }
5657  Utilities->CallLogPop(2137);
5658  return;
5659  }
5660  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5661 
5662  if(LocationName == "")
5663  {
5664  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5665  }
5666  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5667  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5668  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5670 
5671  // determine all positions & exits
5672  if(LocationName != "")
5673  {
5674  // if message given only call at ~5 sec intervals
5676  {
5677  FirstNamedElementPos = LeadElement;
5678  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5679  // check if possible with LeadElement as First
5680  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5681  {
5682  FirstNamedElementPos = MidElement;
5683  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5684  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5685  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5686  {
5688  {
5689  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5691  }
5692  Utilities->CallLogPop(1009);
5693  return;
5694  }
5695  }
5696  else
5697  {
5698  // if first is possible then check if all 4 positions at location, and if not try the second
5699  int LeadPosA = FirstNamedElementPos;
5700  int LeadPosB = FirstNamedLinkedElementPos;
5701  int LeadPosC = SecondNamedElementPos;
5702  int LeadPosD = SecondNamedLinkedElementPos;
5703  // count number of positions that are at the location
5704  int LeadNumAtLoc = 0;
5705  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5706  {
5707  LeadNumAtLoc++;
5708  }
5709  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5710  {
5711  LeadNumAtLoc++;
5712  }
5713  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5714  {
5715  LeadNumAtLoc++;
5716  }
5717  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5718  {
5719  LeadNumAtLoc++;
5720  }
5721  if(LeadNumAtLoc < 4)
5722  {
5723  FirstNamedElementPos = MidElement;
5724  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5725  SecondNamedLinkedElementPos)) // restore originals
5726  {
5727  FirstNamedElementPos = LeadPosA;
5728  FirstNamedLinkedElementPos = LeadPosB;
5729  SecondNamedElementPos = LeadPosC;
5730  SecondNamedLinkedElementPos = LeadPosD;
5731  }
5732  else // count number at location
5733  {
5734  int MidNumAtLoc = 0;
5735  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5736  {
5737  MidNumAtLoc++;
5738  }
5739  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5740  {
5741  MidNumAtLoc++;
5742  }
5743  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5744  {
5745  MidNumAtLoc++;
5746  }
5747  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5748  {
5749  MidNumAtLoc++;
5750  }
5751  if(LeadNumAtLoc > MidNumAtLoc)
5752  // change back, else keep new values
5753  {
5754  FirstNamedElementPos = LeadPosA;
5755  FirstNamedLinkedElementPos = LeadPosB;
5756  SecondNamedElementPos = LeadPosC;
5757  SecondNamedLinkedElementPos = LeadPosD;
5758  }
5759  }
5760  }
5761  }
5762  }
5763  else
5764  {
5765  Utilities->CallLogPop(1791);
5766  return;
5767  }
5768  }
5769  else
5770  {
5771  throw Exception("Error - LocationName not set in FrontTrainSplit");
5772  }
5773  // set front & rear train parameters
5774  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5775  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5776  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5777  if(LeadElement == FirstNamedElementPos)
5778  {
5779  if(MidElement == SecondNamedElementPos)
5780  {
5781  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5782  FrontTrainRearPosition = FirstNamedElementPos;
5783  RearTrainFrontPosition = SecondNamedElementPos;
5784  RearTrainRearPosition = SecondNamedLinkedElementPos;
5785  }
5786  else // MidElement must == FirstNamedLinkedElementPos
5787  {
5788  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5789  FrontTrainRearPosition = SecondNamedElementPos;
5790  RearTrainFrontPosition = FirstNamedElementPos;
5791  RearTrainRearPosition = FirstNamedLinkedElementPos;
5792  }
5793  }
5794  else // MidElement == FirstNamedElementPos
5795  {
5796  if(LeadElement == SecondNamedElementPos)
5797  {
5798  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5799  FrontTrainRearPosition = SecondNamedElementPos;
5800  RearTrainFrontPosition = FirstNamedElementPos;
5801  RearTrainRearPosition = FirstNamedLinkedElementPos;
5802  }
5803  else // LeadElement must == FirstNamedLinkedElementPos
5804  {
5805  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5806  FrontTrainRearPosition = FirstNamedElementPos;
5807  RearTrainFrontPosition = SecondNamedElementPos;
5808  RearTrainRearPosition = SecondNamedLinkedElementPos;
5809  }
5810  }
5811  RearTrainExitPos = -1;
5812  for(int x = 0; x < 4; x++)
5813  {
5814  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5815  {
5816  RearTrainExitPos = x;
5817  break;
5818  }
5819  }
5820  if(RearTrainExitPos == -1)
5821  {
5822  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5823  }
5824  FrontTrainExitPos = -1;
5825  for(int x = 0; x < 4; x++)
5826  {
5827  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5828  {
5829  FrontTrainExitPos = x;
5830  break;
5831  }
5832  }
5833  if(FrontTrainExitPos == -1)
5834  {
5835  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5836  }
5837  // check no train (apart from self) on any of the 4 elements & fail if so
5838  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5839  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5840 
5841  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5842  {
5843  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5844  }
5845  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5846  {
5847  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5848  }
5849  else
5850  {
5851  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5852  }
5853  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5854  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5855  // can't be a bridge
5856  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5857  // can't be a bridge
5858  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5859  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5860 
5861  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5862  {
5863  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5864  }
5865  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5866  {
5867  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5868  }
5869  else
5870  {
5871  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5872  }
5873  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5874  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5875  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5876  {
5878  {
5881  }
5882  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5883  Utilities->CallLogPop(1010);
5884  return;
5885  }
5887  {
5889  }
5890  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5891  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5892  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5893  // variable as it is needed for setting up the new train
5894  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5895 
5896  UnplotTrain(0);
5897  StartSpeed = 0;
5898  RearStartElement = RearTrainRearPosition;
5899  RearStartExitPos = RearTrainExitPos;
5900  StoppedAtLocation = true;
5901  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5902  {
5903  StoppedWithoutPower = true;
5904  }
5905  PlotStartPosition(3);
5910 
5911  Mass = Mass / 2;
5912  MaxBrakeRate = MaxBrakeRate / 2;
5913  PowerAtRail = PowerAtRail / 2;
5914  AValue = sqrt(2 * PowerAtRail / Mass);
5915  // shouldn't change but include in case not set earlier
5916 
5917  // create new front train
5918 /*
5919  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5920  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5921  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5922 */
5923  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5924  TActionEventType EventType = NoEvent;
5925 
5926  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5927  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5928  // false for SignallerControl
5929  {
5930  Utilities->CallLogPop(1721); // EventType not used here
5931  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5932  // another train, in which case a message will have been sent to the perf log, also might well clear later
5933  // when other train moves away
5934  return;
5935  }
5936  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5937  // see mods in UpdateTrain for v1.3.2
5938  TrainController->TrainAdded = true;
5939 
5940  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5941 
5942  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5943  TTOD.RunningEntry = Running;
5944  Utilities->CallLogPop(998);
5945 }
5946 
5947 // ---------------------------------------------------------------------------
5948 
5949 void TTrain::RearTrainSplit(int Caller)
5950 {
5951 /*
5952  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5953  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5954  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5955  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5956 */
5957  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5958  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5959  if(PowerAtRail < 1)
5960  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5961  {
5963  {
5964  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5965  }
5967  Utilities->CallLogPop(2138);
5968  return;
5969  }
5970  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5971 
5972  if(LocationName == "")
5973  {
5974  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5975  }
5976  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5977  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5978  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5980 
5981  // determine all positions & exits
5982  if(LocationName != "")
5983  {
5984  // if message given only call at ~5 sec intervals
5986  {
5987  FirstNamedElementPos = LeadElement;
5988  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5989  SecondNamedLinkedElementPos))
5990  {
5991  FirstNamedElementPos = MidElement;
5992  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5993  SecondNamedLinkedElementPos))
5994  {
5996  {
5997  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5999  }
6000  Utilities->CallLogPop(1013);
6001  return;
6002  }
6003  }
6004  else
6005  {
6006  // if first is possible then check if all 4 positions at location, and if not try the second
6007  int LeadPosA = FirstNamedElementPos;
6008  int LeadPosB = FirstNamedLinkedElementPos;
6009  int LeadPosC = SecondNamedElementPos;
6010  int LeadPosD = SecondNamedLinkedElementPos;
6011  // count number of positions that are at the location
6012  int LeadNumAtLoc = 0;
6013  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
6014  {
6015  LeadNumAtLoc++;
6016  }
6017  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
6018  {
6019  LeadNumAtLoc++;
6020  }
6021  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
6022  {
6023  LeadNumAtLoc++;
6024  }
6025  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
6026  {
6027  LeadNumAtLoc++;
6028  }
6029  if(LeadNumAtLoc < 4)
6030  {
6031  FirstNamedElementPos = MidElement;
6032  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6033  SecondNamedLinkedElementPos)) // restore originals
6034  {
6035  FirstNamedElementPos = LeadPosA;
6036  FirstNamedLinkedElementPos = LeadPosB;
6037  SecondNamedElementPos = LeadPosC;
6038  SecondNamedLinkedElementPos = LeadPosD;
6039  }
6040  else // count number at location
6041  {
6042  int MidNumAtLoc = 0;
6043  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
6044  {
6045  MidNumAtLoc++;
6046  }
6047  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6048  {
6049  MidNumAtLoc++;
6050  }
6051  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6052  {
6053  MidNumAtLoc++;
6054  }
6055  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6056  {
6057  MidNumAtLoc++;
6058  }
6059  if(LeadNumAtLoc > MidNumAtLoc)
6060  // change back, else keep new values
6061  {
6062  FirstNamedElementPos = LeadPosA;
6063  FirstNamedLinkedElementPos = LeadPosB;
6064  SecondNamedElementPos = LeadPosC;
6065  SecondNamedLinkedElementPos = LeadPosD;
6066  }
6067  }
6068  }
6069  }
6070  }
6071  else
6072  {
6073  Utilities->CallLogPop(1792);
6074  return;
6075  }
6076  }
6077  else
6078  {
6079  throw Exception("Error - LocationName not set in RearTrainSplit");
6080  }
6081  // set front & rear train parameters
6082  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
6083  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
6084  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
6085  if(LeadElement == FirstNamedElementPos)
6086  {
6087  if(MidElement == SecondNamedElementPos)
6088  {
6089  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6090  FrontTrainRearPosition = FirstNamedElementPos;
6091  RearTrainFrontPosition = SecondNamedElementPos;
6092  RearTrainRearPosition = SecondNamedLinkedElementPos;
6093  }
6094  else // MidElement must == FirstNamedLinkedElementPos
6095  {
6096  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6097  FrontTrainRearPosition = SecondNamedElementPos;
6098  RearTrainFrontPosition = FirstNamedElementPos;
6099  RearTrainRearPosition = FirstNamedLinkedElementPos;
6100  }
6101  }
6102  else // MidElement == FirstNamedElementPos
6103  {
6104  if(LeadElement == SecondNamedElementPos)
6105  {
6106  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6107  FrontTrainRearPosition = SecondNamedElementPos;
6108  RearTrainFrontPosition = FirstNamedElementPos;
6109  RearTrainRearPosition = FirstNamedLinkedElementPos;
6110  }
6111  else // LeadElement must == FirstNamedLinkedElementPos
6112  {
6113  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6114  FrontTrainRearPosition = FirstNamedElementPos;
6115  RearTrainFrontPosition = SecondNamedElementPos;
6116  RearTrainRearPosition = SecondNamedLinkedElementPos;
6117  }
6118  }
6119  RearTrainExitPos = -1;
6120  for(int x = 0; x < 4; x++)
6121  {
6122  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
6123  {
6124  RearTrainExitPos = x;
6125  break;
6126  }
6127  }
6128  if(RearTrainExitPos == -1)
6129  {
6130  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
6131  }
6132  FrontTrainExitPos = -1;
6133  for(int x = 0; x < 4; x++)
6134  {
6135  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
6136  {
6137  FrontTrainExitPos = x;
6138  break;
6139  }
6140  }
6141  if(FrontTrainExitPos == -1)
6142  {
6143  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
6144  }
6145  // check no train (apart from self) on any of the 4 elements & fail if so
6146  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
6147  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
6148 
6149  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
6150  {
6151  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
6152  }
6153  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
6154  {
6155  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
6156  }
6157  else
6158  {
6159  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
6160  }
6161  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
6162  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
6163  // can't be a bridge
6164  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
6165  // can't be a bridge
6166  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
6167  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
6168 
6169  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
6170  {
6171  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
6172  }
6173  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
6174  {
6175  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
6176  }
6177  else
6178  {
6179  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
6180  }
6181  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
6182  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
6183  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
6184  {
6186  {
6189  }
6190  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
6191  Utilities->CallLogPop(1014);
6192  return;
6193  }
6195  {
6197  }
6198  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
6199  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
6200  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
6201  // variable as it is needed for setting up the new train
6202  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
6203 
6204  UnplotTrain(1);
6205  StartSpeed = 0;
6206  RearStartElement = FrontTrainRearPosition;
6207  RearStartExitPos = FrontTrainExitPos;
6208  StoppedAtLocation = true;
6209  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
6210  {
6211  StoppedWithoutPower = true;
6212  }
6213  PlotStartPosition(4);
6218  Mass = Mass / 2;
6219  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6220  MaxBrakeRate = MaxBrakeRate / 2;
6221  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6222  PowerAtRail = PowerAtRail / 2;
6223  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6224  AValue = sqrt(2 * PowerAtRail / Mass);
6225  // shouldn't change but include in case not set earlier
6226 
6227  // create new rear train
6228 /*
6229  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6230  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6231  int RepeatNumber, int IncrementalMinutes)
6232 */
6233  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6234  TActionEventType EventType = NoEvent;
6235 
6236  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
6237  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6238  // false for SignallerControl
6239  {
6240  Utilities->CallLogPop(1722); // EventType not used here
6241  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6242  // another train, in which case a message will have been sent to the perf log, also might well clear later
6243  // when other train moves away
6244  return;
6245  }
6246  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6247  // see mods in UpdateTrain for v1.3.2
6248  TrainController->TrainAdded = true;
6249  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6250 
6251  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6252  TTOD.RunningEntry = Running;
6253  Utilities->CallLogPop(1015);
6254 }
6255 
6256 // ---------------------------------------------------------------------------
6257 
6258 void TTrain::FinishJoin(int Caller)
6259 {
6260  if(FinishJoinLogSent == false)
6261  {
6262  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6263  FinishJoinLogSent = true; // so don't keep logging this event
6264  // don't need to reset it to false after the event as the train is deleted
6265  }
6266  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6267  if(TrainFailed)
6268  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6269  {
6271  {
6272  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6273  }
6275  Utilities->CallLogPop(2139);
6276  return;
6277  }
6278  if(TrainGone)
6279  // this means that the train has already joined the other & is awaiting deletion by TrainController
6280  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6281  // on from jbo & TrainToJoinIsAdjacent returns false
6282  {
6283  Utilities->CallLogPop(1035);
6284  return;
6285  }
6286  TTrain *TrainToJoin;
6288 
6289  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6290  {
6292  {
6293  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6296  }
6297  Utilities->CallLogPop(1030);
6298  return; // keep this here in case need to add code before final return
6299  }
6300  // no need to clear error report flag here, cleared in jbo function
6301  // No need to set TimetableFinished, done in jbo function
6302  Utilities->CallLogPop(1031);
6303 }
6304 
6305 // ---------------------------------------------------------------------------
6306 
6307 void TTrain::JoinedBy(int Caller)
6308 {
6309  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6310  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6311  if(PowerAtRail < 1)
6312  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6313  {
6315  {
6316  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6317  }
6319  Utilities->CallLogPop(2140);
6320  return;
6321  }
6322  TTrain *TrainToBeJoinedBy;
6324 
6325  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6326  {
6328  {
6329  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6332  }
6333  LastActionDelayFlag = true;
6334  // need to update LastActionTime if this train first to arrive as need 30s after
6335  // both adjacent before the join
6336  Utilities->CallLogPop(1032);
6337  return;
6338  }
6339  // here when other train is adjacent
6341  {
6343  // need to update this as need 30s after both adjacent before the join
6344  LastActionDelayFlag = false;
6345  Utilities->CallLogPop(1033);
6346  return;
6347  }
6348  // here when other train is adjacent & 30 secs elapsed since both adjacent
6349 
6350  // set new values for mass etc
6351  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6352  {
6353  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6354  }
6355  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6356  double OwnBrakeForce = MaxBrakeRate * Mass;
6357  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6358 
6359  Mass += TrainToBeJoinedBy->Mass;
6360  MaxBrakeRate = CombinedBrakeRate;
6361  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6362  AValue = sqrt(2 * PowerAtRail / Mass);
6363 
6365  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6366  TrainToBeJoinedBy->TimetableFinished = true;
6367  TrainToBeJoinedBy->TrainGone = true;
6368  // this will cause other train to be deleted
6369  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6373  Utilities->CallLogPop(1034);
6374 }
6375 
6376 // ---------------------------------------------------------------------------
6377 
6378 void TTrain::ChangeTrainDirection(int Caller, bool NoLogFlag)
6379 {
6380  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6381  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6382  if(PowerAtRail < 1)
6383  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6384  {
6386  {
6387  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6388  }
6389  ZeroPowerNoCDTMessage = true;
6390  Utilities->CallLogPop(2141);
6391  return;
6392  }
6393  TColor TempColour = BackgroundColour;
6394 
6395  UnplotTrain(2);
6398  StartSpeed = 0;
6399  StoppedAtLocation = true;
6400  PlotStartPosition(1);
6401  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6402  // plot same as was - should always be pale green
6403  if(!NoLogFlag)
6404  {
6407  }
6409 
6410  //now erase a stub route if there is one, added at v2.5.1
6411  //first element of route is now immediately behind the train (i.e. next to MidElement)
6412  if(MidEntryPos >= 0)
6413  {
6414  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6415  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6416  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6417  int RouteNumber = -1;
6418  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6419  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6420  {
6421  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6422  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
6423  //elements can continue to be removed from that route
6424  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6425  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6426  {
6427  bool FirstPass = true; //added at v2.8.0
6428  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6429  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
6430  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6431  int TVPos2 = PDE.GetTrackVectorPosition();
6432  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6433  {
6434  break;
6435  }
6436  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6438  {
6439  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6440  }
6441  else
6442  {
6443  break;
6444  }
6445  FirstPass = false;
6446  }
6447  AllRoutes->RebuildRailwayFlag = true;
6448  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6449  }
6450  }
6451  }
6452  Utilities->CallLogPop(1012);
6453 }
6454 
6455 // ---------------------------------------------------------------------------
6456 
6457 void TTrain::NewTrainService(int Caller, bool NoLogFlag) //, bool NoLogFlag added at v2.12.0 for new service tt skips
6458 // change to new train, give new service message
6459 //same RepeatNumber used for the new service
6460 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6461  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6462  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6463  if(PowerAtRail < 1)
6464  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6465  {
6467  {
6468  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6469  }
6471  Utilities->CallLogPop(2142);
6472  return;
6473  }
6475 
6476  if(!NoLogFlag)
6477  {
6479  }
6480  UnplotTrain(3);
6483  StartSpeed = 0;
6488  HeadCode = NewHeadCode;
6489  StoppedAtLocation = true;
6490  PlotStartPosition(5);
6492  // pale green
6495  TerminatedMessageSent = false;
6496  Utilities->CallLogPop(1022);
6497 }
6498 
6499 // ---------------------------------------------------------------------------
6500 
6501 void TTrain::RemainHere(int Caller)
6502 {
6503  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6504  if(RemainHereLogNotSent) // to prevent repeated logs
6505  {
6506  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6507  RemainHereLogNotSent = false;
6508  }
6510  {
6511  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
6514  TerminatedMessageSent = true;
6515  }
6516  TimetableFinished = true;
6517  Utilities->CallLogPop(1023);
6518 }
6519 
6520 // ---------------------------------------------------------------------------
6521 
6522 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6523 /*
6524  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6525  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6526  except where an action is a departure, starting at the current value for the pointer
6527  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6528  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6529 */{
6530  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6531  {
6532  return; // if remove train that starts under signaller control no messages needed
6533 
6534  }
6535  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6536  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6537  if(IncNum > 0)
6538  {
6539  for(int x = 0; x < IncNum; x++)
6540  {
6541  if(x > 0)
6542  {
6543  Ptr++;
6544  }
6545  // arrival - no need to test for termination as this section only covers missed actions up to the
6546  // arrival point - may terminate later but that not missed
6547  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6548  {
6550  }
6551  // arrival & departure
6552  if(Ptr->FormatType == TimeTimeLoc)
6553  {
6555  }
6556  // departure
6557  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6558  {
6559  continue; // skip TimeLoc departures, message given for arrivals
6560  }
6561  // pass
6562  else if(Ptr->FormatType == PassTime)
6563  {
6565  }
6566  // split
6567  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6568  {
6570  }
6571  // jbo
6572  else if(Ptr->Command == "jbo")
6573  {
6575  }
6576  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6577  // be starts, finishes or cdt
6578  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6579  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6580  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6581  (Ptr->FormatType == Repeat))
6582  {
6583  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6584  }
6585  }
6586  }
6587  else
6588  {
6589  bool IncludeFER = false;
6590  if(IncNum == -1)
6591  {
6592  IncludeFER = true;
6593  }
6594  while(true) // finish commands & repeats break out of loop
6595  {
6596  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6597  if(!IncludeFER && (Ptr->Command == "Fer"))
6598  {
6599  break;
6600  }
6601  // Fer & included
6602  else if(IncludeFER && (Ptr->Command == "Fer"))
6603  {
6605  break;
6606  }
6607  // Repeat
6608  else if(Ptr->FormatType == Repeat)
6609  {
6610  break;
6611  }
6612  // Fjo
6613  else if(Ptr->Command == "Fjo")
6614  {
6616  break;
6617  }
6618  // Frh
6619  else if(Ptr->Command == "Frh")
6620  {
6622  {
6624  TerminatedMessageSent = true;
6625  }
6626  break;
6627  }
6628  // Frh-sh
6629  else if(Ptr->Command == "Frh-sh")
6630  {
6632  {
6634  TerminatedMessageSent = true;
6635  }
6636  break;
6637  }
6638  // Fns, F-nshs, Fns-sh
6639  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6640  {
6642  break;
6643  }
6644  // end of breakout actions
6645  // arrival
6646  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6647  {
6648  if(IsTrainTerminating(1))
6649  {
6651  TerminatedMessageSent = true;
6652  }
6653  else
6654  {
6656  }
6657  }
6658  // arrival & departure
6659  else if(Ptr->FormatType == TimeTimeLoc)
6660  {
6662  }
6663  // departure
6664  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6665  {
6666  Ptr++;
6667  continue; // skip TimeLoc departures, message given for arrivals
6668  }
6669  // pass
6670  else if(Ptr->FormatType == PassTime)
6671  {
6673  }
6674  // split
6675  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6676  {
6678  }
6679  // jbo
6680  else if(Ptr->Command == "jbo")
6681  {
6683  }
6684  // cdt
6685  else if(Ptr->Command == "cdt")
6686  {
6687 // TrainController->LogActionError(25, HeadCode, "", FailMissedChangeDirection, Ptr->LocationName); //commented out at v2.12.0 as cdts not counted as missed events
6688  }
6689  // Errors
6690  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6691  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6692  {
6693  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6694  }
6695  Ptr++;
6696  }
6697  TimetableFinished = true;
6698  }
6699  Utilities->CallLogPop(1021);
6700 }
6701 
6702 // ---------------------------------------------------------------------------
6703 
6704 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6705 // ensure same repeatnumber
6706 {
6707  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6709 
6710  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6711  {
6712  Utilities->CallLogPop(1024);
6713  return(false);
6714  }
6715  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6716  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6717  {
6718  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6719  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6720  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6721  {
6722  Utilities->CallLogPop(1025);
6723  return(true);
6724  }
6725  }
6726  Utilities->CallLogPop(1026);
6727  return(false);
6728 }
6729 
6730 // ---------------------------------------------------------------------------
6731 
6732 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6733 // ensure same repeatnumber
6734 {
6735  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6736  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6737 
6738  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6739  {
6740  Utilities->CallLogPop(1027);
6741  return(false);
6742  }
6743  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6744  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6745  {
6746  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6747  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6748  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6749  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6750  {
6751  Utilities->CallLogPop(1028);
6752  return(true);
6753  }
6754  }
6755  Utilities->CallLogPop(1029);
6756  return(false);
6757 }
6758 
6759 // ---------------------------------------------------------------------------
6760 
6761 void TTrain::NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6762 { //same RepeatNumber (i.e. 0) used for the new shuttle
6763 //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6764  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6765  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6766  if(PowerAtRail < 1)
6767  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6768  {
6770  {
6771  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6772  }
6774  Utilities->CallLogPop(2143);
6775  return;
6776  }
6777  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6778 
6779  if(!NoLogFlag)
6780  {
6782  }
6783  UnplotTrain(4);
6786  StartSpeed = 0;
6791  HeadCode = NewHeadCode;
6792  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6793  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6794  StoppedAtLocation = true;
6795  PlotStartPosition(6);
6797  // pale green
6800  TerminatedMessageSent = false;
6801  Utilities->CallLogPop(1078);
6802 }
6803 
6804 // ---------------------------------------------------------------------------
6805 
6806 void TTrain::RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6807 // need to check whether all repeats finished or not
6808 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6809  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6810  if(RemainHereLogNotSent) // to prevent repeated logs
6811  {
6812  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6813  RemainHereLogNotSent = false;
6814  }
6816  // finished all repeats
6817  {
6819  { //no need to suppress this LogAction because BecomeNewService won't be available in this case
6822  TerminatedMessageSent = true;
6823  // no need to clear message as no more actions
6824  }
6825  TimetableFinished = true;
6826  Utilities->CallLogPop(1080);
6827  return;
6828  }
6829  if(PowerAtRail < 1)
6830  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6831  {
6833  {
6834  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6835  }
6837  Utilities->CallLogPop(2144);
6838  return;
6839  }
6840  int TempRepeatNumber = RepeatNumber + 1;
6841  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6842  // until after LogAction or the wrong time will be used
6843  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6844 
6845  if(!NoLogFlag)
6846  {
6848  }
6849  RepeatNumber++;
6850  UnplotTrain(5);
6853  StartSpeed = 0;
6858  HeadCode = NewHeadCode;
6859  StoppedAtLocation = true;
6860  PlotStartPosition(7);
6862  // pale green
6865  TerminatedMessageSent = false;
6866  Utilities->CallLogPop(1079);
6867 }
6868 
6869 // ---------------------------------------------------------------------------
6870 
6871 void TTrain::RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips
6872 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6873  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6874  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6875  if(PowerAtRail < 1)
6876  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6877  {
6879  {
6880  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6881  }
6883  Utilities->CallLogPop(2145);
6884  return;
6885  }
6887  // finished all repeats
6888  {
6889  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6890  if(!NoLogFlag)
6891  {
6893  }
6894  RepeatNumber = 0;
6895  UnplotTrain(6);
6898  StartSpeed = 0;
6900  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6901  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6903  HeadCode = NewHeadCode;
6904  StoppedAtLocation = true;
6905  PlotStartPosition(9);
6909  TerminatedMessageSent = false;
6910  Utilities->CallLogPop(1081);
6911  return;
6912  }
6913  int TempRepeatNumber = RepeatNumber + 1;
6914  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6915  // until after LogAction or the wrong time will be used
6916  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6917 
6918  if(!NoLogFlag)
6919  {
6921  }
6922  RepeatNumber++;
6923  UnplotTrain(7);
6926  StartSpeed = 0;
6931  HeadCode = NewHeadCode;
6932  StoppedAtLocation = true;
6933  PlotStartPosition(8);
6935  // pale green
6938  TerminatedMessageSent = false;
6939  Utilities->CallLogPop(1082);
6940 }
6941 
6942 // ---------------------------------------------------------------------------
6943 
6945 {
6946  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6947  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6948  // must be preceded by a TimeLoc departure
6949  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6950  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6951  {
6953  {
6954  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6955  {
6956  Utilities->CallLogPop(1083);
6957  return(false);
6958  }
6959  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6960  {
6961  Utilities->CallLogPop(1084);
6962  return(true);
6963  }
6964  }
6965  }
6966  Utilities->CallLogPop(1085);
6967  return(false);
6968 }
6969 
6970 // ---------------------------------------------------------------------------
6971 
6972 bool TTrain::AbleToMove(int Caller)
6973 {
6974  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6975  bool Able = true;
6976 
6977  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0
6978  {
6979  // StoppedForTrainInFront removed as tested below
6980  Utilities->CallLogPop(2146); // added v2.4.0
6981  return(false); // added v2.4.0
6982  }
6983  if(LeadElement > -1)
6984  {
6985  if(Track->TrackElementAt(801, LeadElement).TrackType == Buffers) //moved up here from 'else' below at v2.12.0
6986  {
6987  StoppedForTrainInFront = false;
6988  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6989  Utilities->CallLogPop(2456);
6990  return(false);
6991  }
6992  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6993  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6994  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront) //check if train in front still there
6995  {
6996  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6997  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6998  {
6999  Able = true;
7000  StoppedForTrainInFront = false;
7001  }
7002  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
7003  {
7004  Able = true;
7005  StoppedForTrainInFront = false;
7006  }
7007  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
7008  {
7009  Able = true;
7010  StoppedForTrainInFront = false;
7011  }
7012  else
7013  {
7014  Able = false; //added at v2.12.0 as train still in front so don't want signaller popup options to move available
7015  }
7016  }
7017  }
7018  else // leaving at a continuation so keep going
7019  {
7020  Able = true;
7021  StoppedForTrainInFront = false;
7022  }
7023  Utilities->CallLogPop(1454);
7024  return(Able);
7025 }
7026 
7027 // ---------------------------------------------------------------------------
7028 
7030 {
7031  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
7032  // won't be set; if there is a train then set StoppedForTrainInFront
7033  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
7034  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
7035  if(LeadElement == -1) // exiting at continuation
7036  {
7037  Utilities->CallLogPop(2045);
7038  return(false);
7039  }
7040  // end of addition
7041  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
7042  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
7043 
7044  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
7045  {
7046  StoppedForTrainInFront = true;
7047  Utilities->CallLogPop(1455);
7048  return(false);
7049  }
7050  else
7051  {
7052  Utilities->CallLogPop(1456);
7054  // StoppedWithoutPower added v2.4.0
7055  }
7056 }
7057 
7058 // ---------------------------------------------------------------------------
7059 
7061 {
7062  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
7063  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
7064  TColor TempColour = BackgroundColour;
7065 
7066  UnplotTrain(8);
7069  StartSpeed = 0;
7070  PlotStartPosition(2);
7071  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
7072 
7073  //now erase a stub route if there is one, added at v2.5.1
7074  //first element of route is now immediately behind the train (i.e. next to MidElement)
7075  if(MidEntryPos >= 0)
7076  {
7077  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
7078  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
7079  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
7080  int RouteNumber = -1;
7081  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
7082  if(RouteType == TAllRoutes::NotAutoSigsRoute)
7083  {
7084  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
7085  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
7086  //elements can continue to be removed from that route
7087  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
7088  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
7089  {
7090  bool FirstPass = true; //added at v2.8.0
7091  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
7092  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
7093  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
7094  int TVPos2 = PDE.GetTrackVectorPosition();
7095  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
7096  {
7097  break;
7098  }
7099  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
7101  {
7102  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
7103  }
7104  else
7105  {
7106  break;
7107  }
7108  FirstPass = false;
7109  }
7110  AllRoutes->RebuildRailwayFlag = true;
7111  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
7112  }
7113  }
7114  }
7115  Utilities->CallLogPop(1102);
7116 }
7117 
7118 // ---------------------------------------------------------------------------
7119 
7121 {
7122  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7123  ",FloatingLabelNextString" + "," + HeadCode);
7124  AnsiString RetStr = "", LocationName = "";
7125 
7126  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7127  {
7128  throw Exception("Error - start entry in FloatingLabelNextString");
7129  }
7130  if(Ptr->FormatType == TimeTimeLoc)
7131  {
7132  if(TrainMode == Timetable)
7133  {
7134  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
7135  // not arrived yet in tt mode
7136  {
7137  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
7138  }
7139  else
7140  {
7141  RetStr = "Depart " + Ptr->LocationName + " at or soon after " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7142  }
7143  }
7144  else // TrainMode == Signaller
7145  {
7146  if(!DepartureTimeSet) // not arrived yet
7147  {
7148  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
7149  }
7150  else
7151  {
7152  RetStr = "Depart " + Ptr->LocationName + " at or soon after " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7153  }
7154  }
7155  }
7156  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7157  {
7158  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
7159  }
7160  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7161  {
7162  RetStr = "Depart " + Ptr->LocationName + " at or soon after " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7163  }
7164  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7165  {
7166  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(46, Ptr->EventTime));
7167  }
7168  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7169  {
7170  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
7171  }
7172  else if(Ptr->Command == "Fns")
7173  {
7174  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7175  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
7176  RetStr = GetNewServiceDepartureInfo(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7177  }
7178  else if(Ptr->Command == "F-nshs")
7179  {
7180  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
7182  RetStr = GetNewServiceDepartureInfo(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7183  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7184  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7185  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7186  }
7187  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7188  {
7189  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7190  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
7191  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7192  RetStr = GetNewServiceDepartureInfo(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7193  }
7194  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7195  {
7196  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7197  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
7198  RetStr = GetNewServiceDepartureInfo(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7199  }
7200  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7201  {
7202  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7203  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
7204  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7205  RetStr = GetNewServiceDepartureInfo(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7206  }
7207  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7208  {
7209  RetStr ="None, train terminated at " + Ptr->LocationName;
7210  }
7211  else if(Ptr->Command == "Frh")
7212  {
7213  RetStr = "None, train terminated at " + Ptr->LocationName;
7214  }
7215  else if(Ptr->Command == "Fer")
7216  {
7217  AnsiString AllowedExits = "";
7218  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
7219  }
7220  else if(Ptr->Command == "Fjo")
7221  {
7222  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7224  }
7225  else if(Ptr->Command == "jbo")
7226  {
7227  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7228  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
7229  }
7230  else if(Ptr->Command == "fsp")
7231  {
7232  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7233  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
7234  }
7235  else if(Ptr->Command == "rsp")
7236  {
7237  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7238  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
7239  }
7240  else if(Ptr->Command == "cdt")
7241  {
7242  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
7243  }
7244  Utilities->CallLogPop(1124);
7245  return(RetStr);
7246 }
7247 
7248 // ---------------------------------------------------------------------------
7249 
7250 AnsiString TTrain::GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
7251 {
7252  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7253  + AnsiString(RptNum) + ",GetNewServiceDepartureInfo," + HeadCode);
7254  AnsiString DepTime = "", EventTime = "";
7255  bool CDTFlag = false; //reports if train changes direction before departs
7256  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7257  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
7258  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
7259  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
7260  {
7261  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
7262  {
7263  TowardsLocation = AVI->LocationName;
7264  }
7265  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
7266  {
7267  TTrackElement TE = Track->TrackElementAt(1452, (AVI->ExitList.front()));
7268  if(TE.ActiveTrackElementName != "")
7269  {
7270  TowardsLocation = TE.ActiveTrackElementName;
7271  }
7272  else
7273  {
7274  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
7275  }
7276  }
7277  }
7278  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7279  {
7280  if(AVI->Command == "cdt")
7281  {
7282  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7283  continue;
7284  }
7285  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7286  {
7287  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
7288  RetStr += "\nNew service splits at " + EventTime;
7289  Utilities->CallLogPop(2234);
7290  return(RetStr);
7291  }
7292  if(AVI->Command == "jbo")
7293  {
7294  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
7295  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
7296  Utilities->CallLogPop(2235);
7297  return(RetStr);
7298  }
7299  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7300  {
7301  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7302  if(CDTFlag)
7303  {
7304  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7305  {
7306  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
7307  }
7308  else
7309  {
7310  RetStr += "\nNew service changes direction then departs at " + DepTime;
7311  }
7312  }
7313  else
7314  {
7315  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7316  {
7317  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
7318  }
7319  else
7320  {
7321  RetStr += "\nNew service departs at " + DepTime;
7322  }
7323  }
7324  Utilities->CallLogPop(2236);
7325  return(RetStr);
7326  }
7327  }
7328  Utilities->CallLogPop(2208);
7329  return(RetStr); //if reach here then RetStr doesn't change
7330 }
7331 
7332 // ---------------------------------------------------------------------------
7333 
7335 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7336 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
7337 {
7338  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7339  ",FloatingTimetableString" + "," + HeadCode);
7340  AnsiString RetStr = "", PartStr = "";
7341  int Count = 0;
7342  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
7343  AnsiString LocName = Ptr->LocationName;
7344 
7345  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7346  // can start in signaller control so exclude this
7347  {
7348  throw Exception("Error - start entry in FloatingTimetableString");
7349  }
7350  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
7351  bool FirstPass = true;
7352  Ptr--; // because incremented at start of loop
7353 
7354  // different first TimeTimeLoc display if in signaller control
7355  do
7356  {
7357  Ptr++;
7358  if((Ptr->FormatType == Repeat) || TimetableFinished)
7359  {
7360  break;
7361  }
7362  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7363  {
7364  AnsiString TrainLoc = "";
7365  if(TrainMode == Timetable)
7366  {
7367  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7368  {
7369  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7370  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7371  {
7372  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7373  }
7374  }
7375  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7376  {
7377  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7378  }
7379  else
7380  {
7381  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7382  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7383  Count++; // because there are 2 entries
7384  }
7385  }
7386  else // TrainMode == Signaller
7387  {
7388  if(DepartureTimeSet)
7389  {
7390  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7391  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7392  {
7393  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7394  }
7395  }
7396  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7397  {
7398  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7399  }
7400  else
7401  {
7402  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7403  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7404  Count++; // because there are 2 entries
7405  }
7406  }
7407  }
7408  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7409  {
7410  AnsiString TrainLoc = "";
7411  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7412  {
7413  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7414  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7415  {
7416  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7417  }
7418  }
7419  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7420  {
7421  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7422  }
7423  else
7424  {
7425  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7426  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7427  Count++; // because there are 2 entries
7428  }
7429  }
7430  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7431  {
7432  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7433  }
7434  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7435  {
7436  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7437  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7438  {
7439  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7440  }
7441  }
7442  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewService)
7443  { //note that TreatPassAsTimeLocDeparture can't be set if have SkippedDeparture
7444  PartStr = Utilities->Format96HHMM(GetTrainTime(47, Ptr->EventTime)) + ": Depart from " + Ptr->LocationName;
7445  }
7446  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7447  {
7448  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7449  }
7450  else if(Ptr->Command == "Fns")
7451  {
7452  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7453  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7454  PartStr = GetNewServiceDepartureInfo(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7455  }
7456  else if(Ptr->Command == "F-nshs")
7457  {
7458  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7459  Ptr->LocationName;
7460  PartStr = GetNewServiceDepartureInfo(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7461  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7462  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7463  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7464  }
7465  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7466  {
7467  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7468  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7469  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7470  PartStr = GetNewServiceDepartureInfo(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7471  }
7472  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7473  {
7474  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7475  +" at " + Ptr->LocationName;
7476  PartStr = GetNewServiceDepartureInfo(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7477  }
7478  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7479  {
7480  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7481  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7482  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7483  PartStr = GetNewServiceDepartureInfo(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7484  }
7485  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7486  {
7487  PartStr = "Terminate at " + Ptr->LocationName;
7488  }
7489  else if(Ptr->Command == "Frh")
7490  {
7491  PartStr = "Terminate at " + Ptr->LocationName;
7492  }
7493  else if(Ptr->Command == "Fer")
7494  {
7495  AnsiString AllowedExits = "";
7496  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7497  }
7498  else if(Ptr->Command == "Fjo")
7499  {
7500  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7501  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7502  }
7503  else if(Ptr->Command == "jbo")
7504  {
7505  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7506  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7507  }
7508  else if(Ptr->Command == "fsp")
7509  {
7510  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7511  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7512  }
7513  else if(Ptr->Command == "rsp")
7514  {
7515  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7516  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7517  }
7518  else if(Ptr->Command == "cdt")
7519  {
7520  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7521  }
7522  if(RetStr != "")
7523  {
7524  RetStr = RetStr + '\n' + PartStr;
7525  }
7526  else
7527  {
7528  RetStr = PartStr;
7529  }
7530  FirstPass = false;
7531  Count++;
7532 
7533  if(SkipDep)
7534  {
7535  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
7536  Ptr--; //it is incremented at the start of the next loop
7537  SkipDep = false;
7538  SkipDepActedOn = true;
7539  }
7540  }
7541  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7542  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7543  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7544  // forward as anyone should wish to see without looking at the full timetable
7545  if(TimetableFinished)
7546  {
7547  if(TrainMode == Timetable)
7548  {
7549  RetStr = "Timetable finished";
7550  }
7551  else
7552  {
7553  RetStr = "No timetable";
7554  }
7555  }
7556  Utilities->CallLogPop(1125);
7557  return("Timetable:\n" + RetStr);
7558 }
7559 
7560 // ---------------------------------------------------------------------------
7561 
7562 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7563 {
7564  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7565  Utilities->SaveFileString(OutFile, HeadCode);
7568  Utilities->SaveFileInt(OutFile, StartSpeed);
7571  Utilities->SaveFileInt(OutFile, RepeatNumber);
7574  Utilities->SaveFileInt(OutFile, Mass);
7577  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7584  Utilities->SaveFileDouble(OutFile, BrakeRate);
7588  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7589  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7590  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7591  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7592  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7593  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7597  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7602  Utilities->SaveFileBool(OutFile, Derailed);
7604  Utilities->SaveFileBool(OutFile, Crashed);
7611  Utilities->SaveFileBool(OutFile, NotInService);
7612  Utilities->SaveFileBool(OutFile, Plotted);
7613  Utilities->SaveFileBool(OutFile, TrainGone);
7614  Utilities->SaveFileBool(OutFile, SPADFlag);
7616  Utilities->SaveFileInt(OutFile, HOffset[0]);
7617  Utilities->SaveFileInt(OutFile, HOffset[1]);
7618  Utilities->SaveFileInt(OutFile, HOffset[2]);
7619  Utilities->SaveFileInt(OutFile, HOffset[3]);
7620  Utilities->SaveFileInt(OutFile, VOffset[0]);
7621  Utilities->SaveFileInt(OutFile, VOffset[1]);
7622  Utilities->SaveFileInt(OutFile, VOffset[2]);
7623  Utilities->SaveFileInt(OutFile, VOffset[3]);
7624  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7625  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7626  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7627  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7628  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7629  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7630  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7631  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7633  Utilities->SaveFileInt(OutFile, (short)Straddle);
7634  Utilities->SaveFileInt(OutFile, NextTrainID);
7635  Utilities->SaveFileInt(OutFile, TrainID);
7636  Utilities->SaveFileInt(OutFile, LeadElement);
7637  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7638  Utilities->SaveFileInt(OutFile, LeadExitPos);
7639  Utilities->SaveFileInt(OutFile, MidElement);
7640  Utilities->SaveFileInt(OutFile, MidEntryPos);
7641  Utilities->SaveFileInt(OutFile, MidExitPos);
7642  Utilities->SaveFileInt(OutFile, LagElement);
7643  Utilities->SaveFileInt(OutFile, LagEntryPos);
7644  Utilities->SaveFileInt(OutFile, LagExitPos);
7645  int ColourNumber;
7646 
7648  {
7649  ColourNumber = 0;
7650  }
7652  {
7653  ColourNumber = 1;
7654  }
7656  {
7657  ColourNumber = 2;
7658  }
7660  {
7661  ColourNumber = 3;
7662  }
7664  {
7665  ColourNumber = 4;
7666  }
7668  {
7669  ColourNumber = 5;
7670  }
7672  {
7673  ColourNumber = 6;
7674  }
7676  {
7677  ColourNumber = 7;
7678  }
7680  {
7681  ColourNumber = 8;
7682  }
7684  {
7685  ColourNumber = 9;
7686  }
7688  {
7689  ColourNumber = 10;
7690  }
7692  {
7693  ColourNumber = 11;
7694  }
7696  {
7697  ColourNumber = 12;
7698  }
7699  else if(BackgroundColour == clTRSBackground)
7700  {
7701  ColourNumber = 13;
7702  }
7704  {
7705  ColourNumber = 14; // added at v2.4.0
7706  }
7707  Utilities->SaveFileInt(OutFile, ColourNumber);
7708 
7709  // additional data
7710  bool ForwardHeadCode;
7711 
7712  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
7713  {
7714  ForwardHeadCode = true;
7715  }
7716  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
7717  else
7718  {
7719  ForwardHeadCode = false;
7720  }
7721  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
7722 
7723  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
7724 
7725  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
7726  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
7727 
7728  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
7729  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
7730  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
7731  // so use the last asterisk position for this - 0 for false & 1 for true
7732  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
7733  AnsiString Marker;
7734 
7736  {
7737  Marker = "*****1";
7738  }
7739  else
7740  {
7741  Marker = "*****0";
7742  }
7743  if(RestoreTimetableLocation == "")
7744  {
7745  Utilities->SaveFileString(OutFile, Marker);
7746  }
7747  else
7748  {
7749  AnsiString CombinedString = RestoreTimetableLocation + Marker;
7750  Utilities->SaveFileString(OutFile, CombinedString);
7751  // RestoreTimetableLocation + marker
7752  }
7753  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
7754  Utilities->CallLogPop(1457);
7755 }
7756 
7757 // ---------------------------------------------------------------------------
7758 
7759 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
7760 {
7761  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
7762  HeadCode = Utilities->LoadFileString(InFile);
7765  StartSpeed = Utilities->LoadFileInt(InFile);
7767  if(SignallerMaxSpeed < 10)
7768  {
7769  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
7770  }
7772  RepeatNumber = Utilities->LoadFileInt(InFile);
7775  Mass = Utilities->LoadFileInt(InFile);
7778  {
7780  }
7781  // above added at v2.1.0 for legacy session files where value may not have been limited
7783  EntrySpeed = Utilities->LoadFileDouble(InFile);
7787  if(TimetableMaxRunningSpeed < 10)
7788  {
7789  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7790  }
7792  if(MaxRunningSpeed < 10)
7793  {
7794  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7795  }
7798  BrakeRate = Utilities->LoadFileDouble(InFile);
7802  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
7803  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
7804  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
7805  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
7806  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
7807  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
7816  Derailed = Utilities->LoadFileBool(InFile);
7818  Crashed = Utilities->LoadFileBool(InFile);
7825  NotInService = Utilities->LoadFileBool(InFile);
7826  Plotted = Utilities->LoadFileBool(InFile);
7827  TrainGone = Utilities->LoadFileBool(InFile);
7828  SPADFlag = Utilities->LoadFileBool(InFile);
7830  HOffset[0] = Utilities->LoadFileInt(InFile);
7831  HOffset[1] = Utilities->LoadFileInt(InFile);
7832  HOffset[2] = Utilities->LoadFileInt(InFile);
7833  HOffset[3] = Utilities->LoadFileInt(InFile);
7834  VOffset[0] = Utilities->LoadFileInt(InFile);
7835  VOffset[1] = Utilities->LoadFileInt(InFile);
7836  VOffset[2] = Utilities->LoadFileInt(InFile);
7837  VOffset[3] = Utilities->LoadFileInt(InFile);
7838  PlotElement[0] = Utilities->LoadFileInt(InFile);
7839  PlotElement[1] = Utilities->LoadFileInt(InFile);
7840  PlotElement[2] = Utilities->LoadFileInt(InFile);
7841  PlotElement[3] = Utilities->LoadFileInt(InFile);
7842  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7843  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7844  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7845  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7847  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7848  NextTrainID = Utilities->LoadFileInt(InFile);
7849  // will be same for all but best to save all anyway
7850  TrainID = Utilities->LoadFileInt(InFile);
7851  LeadElement = Utilities->LoadFileInt(InFile);
7852  LeadEntryPos = Utilities->LoadFileInt(InFile);
7853  LeadExitPos = Utilities->LoadFileInt(InFile);
7854  MidElement = Utilities->LoadFileInt(InFile);
7855  MidEntryPos = Utilities->LoadFileInt(InFile);
7856  MidExitPos = Utilities->LoadFileInt(InFile);
7857  LagElement = Utilities->LoadFileInt(InFile);
7858  LagEntryPos = Utilities->LoadFileInt(InFile);
7859  LagExitPos = Utilities->LoadFileInt(InFile);
7860  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7861 
7862  if(ColourNumber == 0)
7863  {
7865  }
7866  else if(ColourNumber == 1)
7867  {
7869  }
7870  else if(ColourNumber == 2)
7871  {
7873  }
7874  else if(ColourNumber == 3)
7875  {
7877  }
7878  else if(ColourNumber == 4)
7879  {
7881  }
7882  else if(ColourNumber == 5)
7883  {
7885  }
7886  else if(ColourNumber == 6)
7887  {
7889  }
7890  else if(ColourNumber == 7)
7891  {
7893  }
7894  else if(ColourNumber == 8)
7895  {
7897  }
7898  else if(ColourNumber == 9)
7899  {
7901  }
7902  else if(ColourNumber == 10)
7903  {
7905  }
7906  else if(ColourNumber == 11)
7907  {
7909  }
7910  else if(ColourNumber == 12)
7911  {
7913  }
7914  else if(ColourNumber == 13)
7915  {
7917  }
7918  else if(ColourNumber == 14)
7919  {
7920  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7921 
7922  }
7923  // additional data
7925  // sets the BackgroundColour to the loaded value
7926  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7927 
7928  if(ForwardHeadCode)
7929  {
7930  for(int x = 0; x < 4; x++)
7931  {
7933  }
7934  }
7935  else
7936  {
7937  for(int x = 0; x < 4; x++)
7938  {
7939  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7940  }
7941  }
7942  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7943  if(TrainMode == Timetable)
7944  {
7945  if(Crashed)
7946  {
7948  }
7949  else
7950  {
7952  }
7953  }
7954  else
7955  {
7957  }
7959  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7960  if(Straddle == LeadMid)
7961  {
7962  if(LeadElement > -1)
7963  {
7965  }
7966  if(LeadElement > -1)
7967  {
7969  }
7970  if(MidElement > -1)
7971  {
7973  }
7974  if(MidElement > -1)
7975  {
7977  }
7978  }
7979  else if(Straddle == LeadMidLag)
7980  {
7981  if(LeadElement > -1)
7982  {
7984  }
7985  if(MidElement > -1)
7986  {
7988  }
7989  if(MidElement > -1)
7990  {
7992  }
7993  if(LagElement > -1)
7994  {
7996  }
7997  }
7998  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7999 
8000  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
8001  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
8002 
8003  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
8004 
8005  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
8006  if(LeadElement > -1)
8007  // need to include this in case train exiting & no lead element
8008  {
8010  {
8011  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
8012  }
8013  }
8014  AValue = sqrt(2 * PowerAtRail / Mass);
8015 
8016  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
8017 
8018  // possible RestoreTimetableLocation + Marker, where Marker is
8019  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
8020  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
8021  // added at beta v0.2e
8022  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
8023  // name not allowed to include the '*' character
8024  {
8025  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
8026  bool GiveMessagesFalse = false;
8027  bool CheckLocationsExistInRailwayTrue = true;
8028  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
8029  {
8030  // otherwise take no action
8031  RestoreTimetableLocation = Location;
8032  }
8033  }
8034  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
8035 
8036  StoppedWithoutPower = false;
8037  if(Marker[6] == '1')
8038  {
8039  StoppedWithoutPower = true;
8040  }
8041  Utilities->CallLogPop(1458);
8042 }
8043 
8044 // ---------------------------------------------------------------------------
8045 
8046 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
8047 {
8049  {
8050  return(false); // HeadCode
8051 
8052  }
8053  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8054  {
8055  return(false); // RearStartElement
8056 
8057  }
8058  if(!Utilities->CheckFileInt(InFile, 0, 3))
8059  {
8060  return(false); // RearStartExitPos
8061 
8062  }
8063  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8064  {
8065  return(false); // StartSpeed
8066 
8067  }
8068  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8069  {
8070  return(false); // SignallerMaxSpeed
8071 
8072  }
8073  if(!Utilities->CheckFileBool(InFile))
8074  {
8075  return(false); // HoldAtLocationInTTMode
8076 
8077  }
8078  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8079  {
8080  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
8081 
8082  }
8083  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8084  {
8085  return(false); // IncrementalMinutes (max 96 x 60)
8086 
8087  }
8088  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8089  {
8090  return(false); // IncrementalDigits
8091 
8092  }
8093  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8094  {
8095  return(false); // Mass
8096 
8097  }
8098  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
8099  {
8100  return(false);
8101  }
8102  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
8103  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
8104  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8105  {
8106  return(false); // FrontElementLength
8107 
8108  }
8109  if(!Utilities->CheckFileDouble(InFile))
8110  {
8111  return(false); // EntrySpeed
8112 
8113  }
8114  if(!Utilities->CheckFileDouble(InFile))
8115  {
8116  return(false); // ExitSpeedHalf
8117 
8118  }
8119  if(!Utilities->CheckFileDouble(InFile))
8120  {
8121  return(false); // ExitSpeedFull
8122 
8123  }
8124  if(!Utilities->CheckFileDouble(InFile))
8125  {
8126  return(false); // TimetableMaxRunningSpeed
8127 
8128  }
8129  if(!Utilities->CheckFileDouble(InFile))
8130  {
8131  return(false); // MaxRunningSpeed
8132 
8133  }
8134  if(!Utilities->CheckFileDouble(InFile))
8135  {
8136  return(false); // MaxExitSpeed
8137 
8138  }
8139  if(!Utilities->CheckFileDouble(InFile))
8140  {
8141  return(false); // MaxBrakeRate
8142 
8143  }
8144  if(!Utilities->CheckFileDouble(InFile))
8145  {
8146  return(false); // BrakeRate
8147 
8148  }
8149  if(!Utilities->CheckFileDouble(InFile))
8150  {
8151  return(false); // PowerAtRail
8152 
8153  }
8154  if(!Utilities->CheckFileBool(InFile))
8155  {
8156  return(false); // FirstHalfMove
8157 
8158  }
8159  if(!Utilities->CheckFileBool(InFile))
8160  {
8161  return(false); // OneLengthAccelDecel
8162 
8163  }
8164  if(!Utilities->CheckFileDouble(InFile))
8165  {
8166  return(false); // double(EntryTime)
8167 
8168  }
8169  if(!Utilities->CheckFileDouble(InFile))
8170  {
8171  return(false); // double(ExitTimeHalf)
8172 
8173  }
8174  if(!Utilities->CheckFileDouble(InFile))
8175  {
8176  return(false); // double(ExitTimeFull)
8177 
8178  }
8179  if(!Utilities->CheckFileDouble(InFile))
8180  {
8181  return(false); // double(ReleaseTime)
8182 
8183  }
8184  if(!Utilities->CheckFileDouble(InFile))
8185  {
8186  return(false); // double(TRSTime)
8187 
8188  }
8189  if(!Utilities->CheckFileDouble(InFile))
8190  {
8191  return(false); // double(LastActionTime)
8192 
8193  }
8194  if(!Utilities->CheckFileBool(InFile))
8195  {
8196  return(false); // CallingOnFlag
8197 
8198  }
8199  if(!Utilities->CheckFileBool(InFile))
8200  {
8201  return(false); // BeingCalledOn
8202 
8203  }
8204  if(!Utilities->CheckFileBool(InFile))
8205  {
8206  return(false); // DepartureTimeSet
8207 
8208  }
8209  if(!Utilities->CheckFileInt(InFile, 0, 2))
8210  {
8211  return(false); // (short)TrainMode
8212 
8213  }
8214  if(!Utilities->CheckFileBool(InFile))
8215  {
8216  return(false); // TimetableFinished
8217 
8218  }
8219  if(!Utilities->CheckFileBool(InFile))
8220  {
8221  return(false); // LastActionDelayFlag
8222 
8223  }
8224  if(!Utilities->CheckFileBool(InFile))
8225  {
8226  return(false); // SignallerRemoved
8227 
8228  }
8229  if(!Utilities->CheckFileBool(InFile))
8230  {
8231  return(false); // TerminatedMessageSent
8232 
8233  }
8234  if(!Utilities->CheckFileBool(InFile))
8235  {
8236  return(false); // Derailed
8237 
8238  }
8239  if(!Utilities->CheckFileBool(InFile))
8240  {
8241  return(false); // DerailPending
8242 
8243  }
8244  if(!Utilities->CheckFileBool(InFile))
8245  {
8246  return(false); // Crashed
8247 
8248  }
8249  if(!Utilities->CheckFileBool(InFile))
8250  {
8251  return(false); // StoppedAtBuffers
8252 
8253  }
8254  if(!Utilities->CheckFileBool(InFile))
8255  {
8256  return(false); // StoppedAtSignal
8257 
8258  }
8259  if(!Utilities->CheckFileBool(InFile))
8260  {
8261  return(false); // StoppedAtLocation
8262 
8263  }
8264  if(!Utilities->CheckFileBool(InFile))
8265  {
8266  return(false); // SignallerStopped
8267 
8268  }
8269  if(!Utilities->CheckFileBool(InFile))
8270  {
8271  return(false); // StoppedAfterSPAD
8272 
8273  }
8274  if(!Utilities->CheckFileBool(InFile))
8275  {
8276  return(false); // StoppedForTrainInFront
8277 
8278  }
8279  if(!Utilities->CheckFileBool(InFile))
8280  {
8281  return(false); // NotInService
8282 
8283  }
8284  if(!Utilities->CheckFileBool(InFile))
8285  {
8286  return(false); // Plotted
8287 
8288  }
8289  if(!Utilities->CheckFileBool(InFile))
8290  {
8291  return(false); // TrainGone
8292 
8293  }
8294  if(!Utilities->CheckFileBool(InFile))
8295  {
8296  return(false); // SPADFlag
8297 
8298  }
8299  if(!Utilities->CheckFileBool(InFile))
8300  {
8301  return(false); // TimeTimeLocArrived
8302 
8303  }
8304  if(!Utilities->CheckFileInt(InFile, 0, 15))
8305  {
8306  return(false); // HOffset[0]
8307 
8308  }
8309  if(!Utilities->CheckFileInt(InFile, 0, 15))
8310  {
8311  return(false); // HOffset[1]
8312 
8313  }
8314  if(!Utilities->CheckFileInt(InFile, 0, 15))
8315  {
8316  return(false); // HOffset[2]
8317 
8318  }
8319  if(!Utilities->CheckFileInt(InFile, 0, 15))
8320  {
8321  return(false); // HOffset[3]
8322 
8323  }
8324  if(!Utilities->CheckFileInt(InFile, 0, 15))
8325  {
8326  return(false); // VOffset[0]
8327 
8328  }
8329  if(!Utilities->CheckFileInt(InFile, 0, 15))
8330  {
8331  return(false); // VOffset[1]
8332 
8333  }
8334  if(!Utilities->CheckFileInt(InFile, 0, 15))
8335  {
8336  return(false); // VOffset[2]
8337 
8338  }
8339  if(!Utilities->CheckFileInt(InFile, 0, 15))
8340  {
8341  return(false); // VOffset[3]
8342 
8343  }
8344  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8345  {
8346  return(false); // PlotElement[0]
8347 
8348  }
8349  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8350  {
8351  return(false); // PlotElement[1]
8352 
8353  }
8354  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8355  {
8356  return(false); // PlotElement[2]
8357 
8358  }
8359  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8360  {
8361  return(false); // PlotElement[3]
8362 
8363  }
8364  if(!Utilities->CheckFileInt(InFile, 0, 3))
8365  {
8366  return(false); // PlotEntryPos[0]
8367 
8368  }
8369  if(!Utilities->CheckFileInt(InFile, 0, 3))
8370  {
8371  return(false); // PlotEntryPos[1]
8372 
8373  }
8374  if(!Utilities->CheckFileInt(InFile, 0, 3))
8375  {
8376  return(false); // PlotEntryPos[2]
8377 
8378  }
8379  if(!Utilities->CheckFileInt(InFile, 0, 3))
8380  {
8381  return(false); // PlotEntryPos[3]
8382 
8383  }
8384  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8385  {
8386  return(false); // TrainCrashedInto
8387 
8388  }
8389  if(!Utilities->CheckFileInt(InFile, 0, 2))
8390  {
8391  return(false); // (short)Straddle
8392 
8393  }
8394  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8395  {
8396  return(false); // NextTrainID
8397 
8398  }
8399  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8400  {
8401  return(false); // TrainID
8402 
8403  }
8404  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8405  {
8406  return(false); // LeadElement
8407 
8408  }
8409  if(!Utilities->CheckFileInt(InFile, 0, 3))
8410  {
8411  return(false); // LeadEntryPos
8412 
8413  }
8414  if(!Utilities->CheckFileInt(InFile, 0, 3))
8415  {
8416  return(false); // LeadExitPos
8417 
8418  }
8419  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8420  {
8421  return(false); // MidElement
8422 
8423  }
8424  if(!Utilities->CheckFileInt(InFile, 0, 3))
8425  {
8426  return(false); // MidEntryPos
8427 
8428  }
8429  if(!Utilities->CheckFileInt(InFile, 0, 3))
8430  {
8431  return(false); // MidExitPos
8432 
8433  }
8434  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8435  {
8436  return(false); // LagElement
8437 
8438  }
8439  if(!Utilities->CheckFileInt(InFile, 0, 3))
8440  {
8441  return(false); // LagEntryPos
8442 
8443  }
8444  if(!Utilities->CheckFileInt(InFile, 0, 3))
8445  {
8446  return(false); // LagExitPos
8447 
8448  }
8449  if(!Utilities->CheckFileInt(InFile, 0, 14))
8450  {
8451  return(false);
8452  }
8453  // Background colour number //14 is failed colour at v2.4.0
8454  if(!Utilities->CheckFileBool(InFile))
8455  {
8456  return(false); // ForwardHeadCode
8457 
8458  }
8459  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8460  {
8461  return(false); // TrainDataEntryValue
8462 
8463  }
8464  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8465  {
8466  return(false); // ActionVectorEntryValue
8467 
8468  }
8470  {
8471  return(false); // End of train marker + possible RestoreTimetableLocation
8472 
8473  }
8474  // and StoppedWithoutPower flag
8475  return(true);
8476 }
8477 
8478 // ---------------------------------------------------------------------------
8479 
8480 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8481 {
8482  // order below reflects significance so earlier shows first, as may have more than one flag set
8483  // only plot flashing trains when Flash is true
8484 
8485 /*
8486  clCrashedBackground (TColor)0x0000FF red
8487  clDerailedBackground (TColor)0x0000FF red
8488  clSPADBackground (TColor)0x00FFFF yellow
8489  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8490  clCallOnBackground (TColor)0xFF33FF light magenta
8491  clSignalStopBackground (TColor)0x00FF66 green
8492  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8493  clStationStopBackground (TColor)0xCCFFCC pale green
8494  clTRSBackground (TColor)0xFFCCFF light pink
8495  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8496  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8497  clSignallerStopped (TColor)0x99CCFF caramel
8498  clNormalBackground (TColor)0xCCCCCC grey
8499 */
8500 
8501  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8502  bool HideFlashingTrain = true;
8503  // hide it when Flash false so it blinks on and off
8504  // if don't hide it it stays displayed all the time
8505  Graphics::TBitmap *SmallTrainBitmap;
8506 
8507  // NB ensure retain same order as zoomed in order so colours correspond
8509  {
8510  TrainController->CrashWarning = true;
8511  SmallTrainBitmap = RailGraphics->smRed;
8512  }
8514  {
8516  SmallTrainBitmap = RailGraphics->smRed;
8517  }
8519  {
8520  TrainController->SPADWarning = true;
8521  SmallTrainBitmap = RailGraphics->smYellow;
8522  }
8524  {
8526  SmallTrainBitmap = RailGraphics->smOrange;
8527  }
8529  {
8531  SmallTrainBitmap = RailGraphics->smMagenta;
8532  }
8534  {
8536  SmallTrainBitmap = RailGraphics->smBrightGreen;
8537  }
8539  {
8541  SmallTrainBitmap = RailGraphics->smCyan;
8542  }
8544  {
8545  SmallTrainBitmap = RailGraphics->smPaleGreen;
8546  HideFlashingTrain = false;
8547  }
8549  {
8550  SmallTrainBitmap = RailGraphics->smCyan;
8551  HideFlashingTrain = false;
8552  }
8554  {
8555  SmallTrainBitmap = RailGraphics->smLightBlue;
8556  HideFlashingTrain = false;
8557  }
8559  {
8560  SmallTrainBitmap = RailGraphics->smCaramel;
8561  HideFlashingTrain = false;
8562  }
8563  else
8564  {
8565  SmallTrainBitmap = RailGraphics->smBlack; // moving
8566  HideFlashingTrain = false;
8567  }
8568  // now plot the new train
8569  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8570  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8571  {
8572  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8573  }
8574  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8575  {
8576  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8577  }
8578  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8579  {
8580  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8581  }
8585  Utilities->CallLogPop(1459);
8586 }
8587 
8588 // ---------------------------------------------------------------------------
8589 
8591 {
8592  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8593  if(!Display->ZoomOutFlag)
8594  {
8595  Utilities->CallLogPop(1304);
8596  return;
8597  }
8598  for(int y = 0; y < 3; y++)
8599  {
8600  if(OldZoomOutElement[y] > -1)
8601  {
8602  bool FoundFlag = false;
8603  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8604  TTrackElement IATElement1, IATElement2;
8605  // default elements to begin with
8606  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8607  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8608  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8609  if(FoundFlag)
8610  {
8611  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8612  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8613  if(IMPair.first != IMPair.second)
8614  {
8615  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8616  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8617  }
8618  }
8619  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8620  }
8621  }
8622  Utilities->CallLogPop(1305);
8623 }
8624 
8625 // ---------------------------------------------------------------------------
8626 
8627 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8628 {
8629  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8630  LocationName = "";
8631  if(!RevisedStoppedAtLoc())
8632  {
8633  Utilities->CallLogPop(1398);
8634  return(false);
8635  }
8636  if(LeadElement > -1)
8637  {
8639  }
8640  if((LocationName == "") && (MidElement > -1))
8641  {
8642  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8643  }
8644  if((LocationName == "") && (LagElement > -1))
8645  {
8646  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8647  }
8648  if(LocationName == "")
8649  {
8650  throw Exception("Error - Location name not set in TrainAtLocation");
8651  }
8652  Utilities->CallLogPop(1399);
8653  return(true);
8654 }
8655 
8656 // ---------------------------------------------------------------------------
8657 
8658 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8659 {
8660  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8661  for(int x = 0; x < 4; x++)
8662  {
8663  PlotTrainGraphic(7, x, Disp);
8664  }
8665  Utilities->CallLogPop(647);
8666 }
8667 
8668 // ---------------------------------------------------------------------------
8669 
8670 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8671 {
8672  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8673  for(int x = 0; x < 4; x++)
8674  {
8675  if(PlotElement[x] > -1)
8676  {
8677  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8678  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8679  }
8680  }
8681  Utilities->CallLogPop(1708);
8682 }
8683 
8684 // ---------------------------------------------------------------------------
8685 
8686 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8687 {
8688  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8689  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8690  AnsiString(LinkNumber) + "," + HeadCode);
8691 
8692 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8693  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8694  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8695 */
8696 
8697  // note that MidElement always fully occupied
8698  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8699  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8700  {
8701  Utilities->CallLogPop(2005);
8702  return(true);
8703  }
8704  if(Straddle == LeadMid)
8705  {
8706  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
8707  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
8708  {
8709  Utilities->CallLogPop(2006);
8710  return(true);
8711  }
8712  }
8713  else if(Straddle == LeadMidLag)
8714  {
8715  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
8716  // only interested in LeadEntryPos as train not occupying ExitPos yet
8717  {
8718  Utilities->CallLogPop(2007);
8719  return(true);
8720  }
8721  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
8722  // only interested in LagExitPos as train has left EntryPos
8723  {
8724  Utilities->CallLogPop(2008);
8725  return(true);
8726  }
8727  }
8728  Utilities->CallLogPop(2009);
8729  return(false);
8730 }
8731 
8732 // ---------------------------------------------------------------------------
8733 
8734 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
8739 // TimeToExit & ExitPair added for multiplayer
8740 {
8741  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
8742  int DistanceToRedSignal = 0, DistanceToExit = -1;
8743  float TimeToAct = 0, LastTimeToExit = TimeToExit;
8744  TimeToExit = -1;
8745  ExitPair.first = -1;
8746  ExitPair.second = -1;
8747  float MinsEarly = 0; //added at v2.6.1
8748  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
8749  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
8750  float TempTTE;
8751 
8752  if(TrainFailed)
8753  {
8754  Utilities->CallLogPop(2147);
8755  return(0); // time to act now, time to exit set above
8756  }
8757  if(SignallerStopped)
8758  {
8759  Utilities->CallLogPop(2080);
8760  return(-1); //time to exit set above
8761  }
8762  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
8763  {
8764  Utilities->CallLogPop(2266);
8765  return(-1); // time to exit set above
8766  }
8767 
8768  // check if if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
8769  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
8770  /*&& (ExitSpeedFull > 1)*/) //LagElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
8771  {
8772  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
8773  {
8774  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8775  {
8776  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8777  if(TempTTE < LastTimeToExit)
8778  {
8779  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8780  }
8781  else
8782  {
8783  TimeToExit = LastTimeToExit;
8784  }
8785  }
8786  else
8787  {
8788  TimeToExit = LastTimeToExit;
8789  }
8790  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
8791  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
8792  Utilities->CallLogPop(2342);
8793  return(-1);
8794  }
8795  else
8796  {
8797  TimeToExit = 0; //all train exited
8798  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
8799  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
8800  Utilities->CallLogPop(2343);
8801  return(-1);
8802  }
8803  }
8804  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation)/* && (ExitSpeedFull > 1)*/)
8805  //here MidElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
8806  {
8807  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
8808  {
8809  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8810  {
8811  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8812  if(TempTTE < LastTimeToExit)
8813  {
8814  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8815  }
8816  else
8817  {
8818  TimeToExit = LastTimeToExit;
8819  }
8820  }
8821  else
8822  {
8823  TimeToExit = LastTimeToExit;
8824  }
8825  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
8826  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
8827  Utilities->CallLogPop(2344);
8828  return(-1);
8829  }
8830  else //front element of train fully off the exit, one length to exit
8831  {
8832  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8833  {
8834  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8835  if(TempTTE < LastTimeToExit)
8836  {
8837  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8838  }
8839  else
8840  {
8841  TimeToExit = LastTimeToExit;
8842  }
8843  }
8844  else
8845  {
8846  TimeToExit = LastTimeToExit;
8847  }
8848  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
8849  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
8850  Utilities->CallLogPop(2345);
8851  return(-1);
8852  }
8853  }
8854  if(LeadElement > -1)
8855  {
8857  /* && (ExitSpeedFull > 1)*/) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
8858  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
8859  //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
8860  if(Straddle == LeadMidLag)
8861  {
8862  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8863  {
8864  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8865  if(TempTTE < LastTimeToExit)
8866  {
8867  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8868  }
8869  else
8870  {
8871  TimeToExit = LastTimeToExit;
8872  }
8873  }
8874  else
8875  {
8876  TimeToExit = LastTimeToExit;
8877  }
8878  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
8879  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
8880  Utilities->CallLogPop(2346);
8881  return(-1);
8882  }
8883  else
8884  {
8885  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8886  {
8887  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8888  if(TempTTE < LastTimeToExit)
8889  {
8890  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8891  }
8892  else
8893  {
8894  TimeToExit = LastTimeToExit;
8895  }
8896  }
8897  else
8898  {
8899  TimeToExit = LastTimeToExit;
8900  }
8901  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
8902  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
8903  Utilities->CallLogPop(2347);
8904  return(-1);
8905  }
8906  }
8907  }
8908 //here if LeadElement > -1 and its forward connection also > -1
8909 
8910  // calc distance to next red signal
8911  if(!Stopped() || RevisedStoppedAtLoc())
8912  {
8913  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
8914  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
8915  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
8916 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
8917  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
8918  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
8919  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
8920  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
8921  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
8922  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
8923  before the train has stopped the current station is still recognised as a future stop.
8924  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
8925  becomes the signal, and the time to act indication becomes 'NOW'.
8926 */
8927  {
8928  FirstPosToBeMeasured = LeadElement;
8929  FirstEntryPos = LeadEntryPos;
8930  }
8931  float CurrentStopTime; // set to 0 at start of function
8932  float LaterStopTime; // set to 0 at start of function
8933  float RecoverableTime; // set to 0 at start of function
8934  int AvTrackSpeed; // set to zero at start of function
8935  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
8936  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
8937  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
8938 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
8939 //Therefore since need to calculate the time for each in the same way use a generic
8940 
8941  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
8942  {
8943  TimeToExit = -1;
8944  Utilities->CallLogPop(2076);
8945  return(-1);
8946  }
8947 //else one or other is set
8948  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
8949  bool DistanceToExitSet = (DistanceToExit > -1);
8950  int GenericDistance = DistanceToRedSignal;
8951  if(DistanceToExitSet)
8952  {
8953  GenericDistance = DistanceToExit;
8954  }
8955 /* Have MinsDelayed; pos or neg,
8956  CurrentStopTime; pos or zero
8957  LaterStopTime; pos or zero
8958  RecoverableTime; pos or zero
8959 
8960  & from these calculate TotalStopTime. noting that:
8961  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
8962  RecoverableTime always < LaterStopTime or both zero
8963  can't subtract more than RecoverableTime (MinsDelayed > 0)
8964  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
8965  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
8966  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
8967  if running early & stopped at location CurrentStopTime will automatically include the excess
8968 */
8969  float TimeToSubtract, TotalStopTime;
8970  if(MinsDelayed > RecoverableTime)
8971  {
8972  TimeToSubtract = RecoverableTime;
8973  }
8974  else
8975  {
8976  TimeToSubtract = MinsDelayed; // may be negative;
8977  }
8978  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
8979  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
8980  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
8981  //next station stop
8982  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
8983  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
8984  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
8985  //first find departure time from the next stop
8986  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
8987  {
8988  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
8989  {
8992  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8993  }
8994  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
8995  {
8997  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8998  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
8999  {
9000  // must be a departure
9001  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
9002  }
9003  }
9004  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
9005  {
9006  MinsEarly = 0;
9007  }
9008  if(MinsEarly < 0)
9009  {
9010  MinsEarly = 0;
9011  }
9012  }
9013  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
9014  {
9015  if(CurrentStopTime > 0)
9016  {
9017  TotalStopTime = CurrentStopTime + LaterStopTime;
9018  }
9019  // stopped at loc, will depart on time
9020  else
9021  {
9022  TotalStopTime = LaterStopTime - MinsDelayed;
9023  }
9024  // not stopped, will depart on time at first later stop so add the delay
9025  }
9026  else if((MinsEarly > 0) && !Stopped()) //running early
9027  {
9028  TotalStopTime = LaterStopTime + MinsEarly;
9029  }
9030  else // on time or running late
9031  {
9032  if(LaterStopTime == 0)
9033  {
9034  TotalStopTime = CurrentStopTime;
9035  }
9036  // no later stops, if stopped now will depart as soon as possible,
9037  // if not stopped no stop times to add
9038  else
9039  {
9040  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
9041  }
9042  }
9043  if(AvTrackSpeed < 30)
9044  {
9045  AvTrackSpeed = 30;
9046  }
9047  int Speed = AvTrackSpeed;
9048  if(AvTrackSpeed > int(MaxRunningSpeed))
9049  {
9050  Speed = int(MaxRunningSpeed);
9051  }
9052  if(TrainMode == Signaller)
9053  {
9054  Speed = SignallerMaxSpeed;
9055  TotalStopTime = 0;
9056  }
9057  if(DistanceToRedSignalSet)
9058  {
9059  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9060  // accel & decel taken into account in
9061  // CalcDistanceToRedSignalandStopTime
9062  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9063  TimeToExit = -1;
9064  Utilities->CallLogPop(2079);
9065  return(TimeToAct);
9066  }
9067  else //DistanceToExitSet must be true
9068  {
9069  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9070  // accel & decel taken into account in
9071  // CalcDistanceToRedSignalandStopTime
9072  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9073  Utilities->CallLogPop(2370);
9074  return(-1); //no red signal so no time to act
9075  }
9076  }
9077  else // stopped not at location
9078  {
9080  {
9081  TimeToAct = 0.0;
9082  TimeToExit = -1;
9083  }
9084  // but if stopped at a signal & autosigs route after it then ok
9085  if(StoppedAtSignal)
9086  {
9087  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
9088  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
9089  int NextExitPos;
9090  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
9091  {
9092  if((NextEntryPos == 0) || (NextEntryPos == 2))
9093  // leading entry point
9094  {
9095  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
9096  {
9097  NextExitPos = 1;
9098  }
9099  else
9100  {
9101  NextExitPos = 3;
9102  }
9103  }
9104  else
9105  {
9106  NextExitPos = 0; // trailing entry point
9107  }
9108  }
9109  else
9110  {
9111  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
9112  }
9113  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
9114  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
9115  int RouteNumber; // holder for referenced value, not used
9116  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9117  {
9118  TimeToAct = -1;
9119  TimeToExit = -1;
9120  }
9121  }
9122  Utilities->CallLogPop(2074);
9123  return(TimeToAct);
9124  }
9125 }
9126 
9127 // ---------------------------------------------------------------------------
9128 
9130 {
9131  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
9132  if(LeadElement > -1)
9133  {
9135  {
9136  Utilities->CallLogPop(2148);
9137  return(true);
9138  }
9139  }
9140  if(MidElement > -1)
9141  {
9143  {
9144  Utilities->CallLogPop(2149);
9145  return(true);
9146  }
9147  }
9148  if(LagElement > -1)
9149  {
9151  {
9152  Utilities->CallLogPop(2150);
9153  return(true);
9154  }
9155  }
9156  Utilities->CallLogPop(2151);
9157  return(false);
9158 }
9159 
9160 // ---------------------------------------------------------------------------
9161 // TTrainController
9162 // ---------------------------------------------------------------------------
9163 
9165 {
9166  OnTimeArrivals = 0;
9167  LateArrivals = 0;
9168  EarlyArrivals = 0;
9169  OnTimePasses = 0;
9170  LatePasses = 0;
9171  EarlyPasses = 0;
9172  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
9173  LateExits = 0;
9174  EarlyExits = 0;
9175  OnTimeDeps = 0;
9176  LateDeps = 0;
9177  MissedStops = 0;
9178  OtherMissedEvents = 0;
9179  UnexpectedExits = 0;
9180  NumFailures = 0;
9181  IncorrectExits = 0;
9182  SPADEvents = 0;
9183  SPADRisks = 0;
9184  CrashedTrains = 0;
9185  Derailments = 0;
9186  TotArrDepPass = 0;
9187  TotLateArrMins = 0;
9188  TotEarlyArrMins = 0;
9189  TotLatePassMins = 0;
9190  TotEarlyPassMins = 0;
9191  TotLateExitMins = 0; //added at v2.9.1
9192  TotEarlyExitMins = 0; //added at v2.9.1
9193  TotLateDepMins = 0;
9194  ExcessLCDownMins = 0;
9195  SkippedTTEvents = 0; //added at v2.11.0
9196  TTClockTime = 0; // added for v0.6
9198  // added at v1.3.0 to ensure false at start
9199  OpTimeToActUpdateCounter = 0; // new v2.2.0
9200  OpActionPanelVisible = false; // new v2.2.0
9201  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
9202  SSHigh = false;
9203  MRSHigh = false;
9204  MRSLow = false;
9205  MassHigh = false;
9206  BFHigh = false;
9207  BFLow = false;
9208  PwrHigh = false;
9209  SigSHigh = false;
9210  SigSLow = false;
9211  randomize();
9212  // to seed rand() & random() with a random number (see UpdateTrain)
9213 }
9214 
9215 // ---------------------------------------------------------------------------
9216 
9218 {
9219  for(unsigned int x = 0; x < TrainVector.size(); x++)
9220  {
9221  TrainVectorAt(32, x).DeleteTrain(4);
9222  }
9223  TrainVector.clear();
9224 }
9225 
9226 // ---------------------------------------------------------------------------
9227 
9228 void TTrainController::LogEvent(AnsiString Str)
9229 {
9230  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
9231 
9232  // restrict to last 1000 entries
9233  Utilities->EventLog.push_back(FullStr);
9234  if(Utilities->EventLog.size() > 1000)
9235  {
9236  Utilities->EventLog.pop_front();
9237  }
9238 }
9239 
9240 // ---------------------------------------------------------------------------
9241 
9243 {
9244  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
9245  bool ClockState = Utilities->Clock2Stopped;
9246  Utilities->Clock2Stopped = true;
9247  // new section dealing with Snt & Snt-sh additions
9248  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
9249  // clock tick after stops flashing
9251  {
9252  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9253  {
9254  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
9255  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
9256  TActionEventType EventType = NoEvent;
9257  if(AVEntry0.Command == "Snt")
9258  {
9259  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9260  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9261  int IncrementalMinutes = 0;
9262  int IncrementalDigits = 0;
9263  if(AVEntryLast.FormatType == Repeat)
9264  {
9265  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9266  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9267  }
9268  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9269  {
9270  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
9271  }
9272  // see above note
9273 
9274  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9275  {
9276  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
9277  if(TTOD.RunningEntry != NotStarted)
9278  {
9279  continue;
9280  }
9281 
9282 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
9283 //if so and no arrival signalled yet bypass the timetabled arrival
9284 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
9285 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
9286 
9287  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
9288  {
9289  break; // all the rest will also be greater
9290  }
9291  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
9292  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9293  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
9294  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
9295  {
9296  TTOD.TrainID = TrainVector.back().TrainID;
9297  TTOD.RunningEntry = Running;
9298  }
9299  else if(EventType == FailTrainEntry)
9300  {
9301  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9302  }
9303  }
9304  }
9305  if(AVEntry0.Command == "Snt-sh")
9306  // just start this once, shuttle repeats take care of restarts
9307  {
9308  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9309  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9310  int IncrementalMinutes = 0;
9311  int IncrementalDigits = 0;
9312  if(AVEntryLast.FormatType == Repeat)
9313  {
9314  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9315  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9316  }
9317  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9318  {
9319  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
9320  }
9321  // see above note
9322  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
9323  if(TTOD.RunningEntry == NotStarted)
9324  {
9325  if(AVEntry0.EventTime <= TTClockTime)
9326  {
9327  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9328  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
9329  TDEntry.SignallerSpeed, false, EventType))
9330  // false for SignallerControl
9331  {
9332  TTOD.TrainID = TrainVector.back().TrainID;
9333  TTOD.RunningEntry = Running;
9334  }
9335  else if(EventType == FailTrainEntry)
9336  {
9337  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9338  }
9339  }
9340  }
9341  }
9342  }
9343  }
9344 
9345  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
9346  // iteration, next cycle will catch up with any other pending updates
9347  if(!TrainVector.empty())
9348  {
9349  TrainAdded = false;
9350 
9351 //elapsed time investigations
9352 
9353 //elapsed time segment
9354 //double Start, End;
9355 //AnsiString ElapsedTimeReport = "";
9356 //end elasped time segment
9357  AllRoutes->CallonVector.clear(); // this will be rebuilt during the calls to UpdateTrain
9358 //elapsed time segment
9359 //Display->PerformanceLog(-1, "\n Train vector size: " + AnsiString(TrainVector.size()) + '\n');
9360 //Display->PerformanceLog(-1, "Start time list");
9361 //end elapsed time segment
9362  for(unsigned int x = 0; x < TrainVector.size(); x++)
9363  {
9364 //elapsed time segment
9365 //Start = double(GetTime()) * 86400; //secs
9366 //end elapsed time segment
9367  TrainVectorAt(33, x).UpdateTrain(0);
9368 //elapsed time segment
9369 //End = double(GetTime()) * 86400;
9370 //ElapsedTimeReport = TrainVectorAt(-1, x).TrainDataEntryPtr->ServiceReference + AnsiString(" ") + AnsiString(int((End - Start) * 1000)); //msecs
9371 //Display->PerformanceLog(-1, ElapsedTimeReport);
9372 //end elapsed time segment
9373 
9374 //end elapsed time investigations
9375 
9376  /* added HasTrainGone() condition below in v0.4c to prevent 2 trains both having TrainGone set in UpdateTrain
9377  at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
9378  iterates in reverse to erase the second train to have gone (when the first train to have gone comes before the second in TrainVector),
9379  but afterwards ReplotTrains iterates forwards and therefore replots the first train to have gone and therefore sets the TrainIDOnElement value
9380  to the exited train, with nothing to reset it. Hovering the mouse over that element with train information enabled causes an error because
9381  the track element thinks the train is still there, whereas it is missing from the TrainVector. BUT subsequently (in v2.11.1) changed RePlotTrains
9382  so it doesn't plot trains with TrainGone set, but left this is as does no harm
9383 
9384  Had another error notified by Kevin Smith on 02/01/22 where a train was manually removed in the same clock cycle as a train exited, and this caused
9385  the same error as above. Did a lot of experimenting but eventually cured it with two changes, first as above in RePlotTrains, and also below adding
9386  a break; command after one TrainHasGone() dealt with. There were introduced in v2.11.1 & seems ok now
9387 
9388  These changes should deal with any number of TrainGone flags set in the same clock cycle - from exiting, manual removal, or joins
9389  */
9390  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
9391  {
9392  break; //only one exited train will be dealt with at a time (see below) so no point looking further
9393  }
9394  }
9395  // set warning flags
9396  CrashWarning = false;
9397  DerailWarning = false;
9398  SPADWarning = false;
9399  CallOnWarning = false;
9400  SignalStopWarning = false;
9401  BufferAttentionWarning = false;
9402  TrainFailedWarning = false;
9403  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
9404  {
9405  TTrain &Train = TrainVectorAt(34, x);
9406  if(Train.Crashed)
9407  // can't use background colours for crashed & derailed because same colour
9408  {
9409  CrashWarning = true;
9410  }
9411  else if(Train.Derailed)
9412  // can't use background colours for crashed & derailed because same colour
9413  {
9414  DerailWarning = true;
9415  }
9416  else if(Train.BackgroundColour == clSPADBackground)
9417  // use colour as that changes as soon as passes signal
9418  {
9419  SPADWarning = true;
9420  }
9421  else if(Train.BackgroundColour == clTrainFailedBackground)
9422  {
9423  TrainFailedWarning = true;
9424  }
9425  else if(Train.BackgroundColour == clCallOnBackground)
9426  // use colour as also stopped at signal
9427  {
9428  CallOnWarning = true;
9429  }
9430  else if(Train.BackgroundColour == clSignalStopBackground)
9431  // use colour to distinguish from call-on
9432  {
9433  SignalStopWarning = true;
9434  }
9435  else if(Train.BackgroundColour == clBufferAttentionNeeded)
9436  // use colour to distinguish from ordinary buffer stop
9437  {
9438  BufferAttentionWarning = true;
9439  }
9440  if(Train.HasTrainGone())
9441  {
9442  AnsiString Loc = "";
9443  bool ElementFound = false;
9444  TTrackElement TE;
9445  if(Train.LagElement > -1)
9446  {
9447  TE = Track->TrackElementAt(531, Train.LagElement);
9448  ElementFound = true;
9449  }
9450  else if(Train.MidElement > -1)
9451  {
9452  TE = Track->TrackElementAt(779, Train.MidElement);
9453  ElementFound = true;
9454  }
9455  else if(Train.LeadElement > -1)
9456  {
9457  TE = Track->TrackElementAt(780, Train.LeadElement);
9458  ElementFound = true;
9459  }
9460  if(ElementFound)
9461  {
9462  if(TE.ActiveTrackElementName != "")
9463  {
9464  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9465  }
9466  else
9467  {
9468  Loc = "track element " + TE.ElementID;
9469  }
9470  }
9471  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
9472  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
9473  // need above first because may also have ActionVectorEntryPtr == "Fer"
9474  {
9475  Train.UnplotTrain(9);
9476  // added at v1.3.0 to reset signals after train removed from an autosigsroute
9478  {
9481  }
9482  // end of addition
9483  AllRoutes->RebuildRailwayFlag = true;
9484  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
9485  // correctly after a crash
9486  }
9487  else if(AVEntryPtr->Command == "Fer")
9488  {
9489  bool CorrectExit = false;
9490  if(!AVEntryPtr->ExitList.empty())
9491  {
9492  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
9493  {
9494  if(*ELIT == Train.LagElement)
9495  {
9496  CorrectExit = true;
9497  }
9498  }
9499  }
9500  if(CorrectExit)
9501  {
9502  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
9503  }
9504  else
9505  {
9506  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
9507  }
9508  }
9509  else
9510  {
9511  if(!AVEntryPtr->SignallerControl)
9512  {
9513  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
9514  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
9515  // -2 is marker for send messages for all remaining actions except Fer if present
9516  }
9517  else
9518  {
9519  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
9520  }
9521  }
9522  Utilities->CumulativeDelayedRandMinsAllTrains += Train.CumulativeDelayedRandMinsOneTrain; //added at v2.13.0 for random delays
9523  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
9524  Train.DeleteTrain(1);
9525  TrainVector.erase(TrainVector.begin() + x);
9526  ReplotTrains(1, Display); //to reset ElementIDs for remaining trains when have removed a train
9527  //NB: won't plot any trains with TrainGone flag set (changed at v2.11.1)
9528  break; //added at v2.11.1 to ensure that only one train with TrainGone set is dealt with in one clock cycle
9529  }
9530  }
9531  }
9532  else
9533  {
9534  // reset all flags in case last train removed with flag set
9535  CrashWarning = false;
9536  DerailWarning = false;
9537  SPADWarning = false;
9538  CallOnWarning = false;
9539  SignalStopWarning = false;
9540  BufferAttentionWarning = false;
9541  TrainFailedWarning = false;
9542  }
9543  // update OpTimeToActMultimap
9545  {
9547  // clears entries then adds values for running trains then for continuation entries
9549  //added for multiplayer for running trains only
9550  }
9551  Utilities->Clock2Stopped = ClockState;
9552  Utilities->CallLogPop(723);
9553 }
9554 
9555 // ---------------------------------------------------------------------------
9557 {
9558  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
9559  if(!TrainVector.empty())
9560  {
9561  for(int x = TrainVector.size() - 1; x >= 0; x--)
9562  {
9563  TrainVectorAt(50, x).DeleteTrain(2);
9564  }
9565  TrainVector.clear();
9566  }
9567  if(!TrainDataVector.empty())
9568  {
9569  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9570  {
9571  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
9572  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9573  {
9574  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
9575  TOD.RunningEntry = NotStarted;
9576  TOD.TrainID = -1;
9577  TOD.EventReported = NoEvent;
9578  }
9579  }
9580  }
9581  Display->GetOutputLog1()->Caption = "";
9582  Display->GetOutputLog2()->Caption = "";
9583  Display->GetOutputLog3()->Caption = "";
9584  Display->GetOutputLog4()->Caption = "";
9585  Display->GetOutputLog5()->Caption = "";
9586  Display->GetOutputLog6()->Caption = "";
9587  Display->GetOutputLog7()->Caption = "";
9588  Display->GetOutputLog8()->Caption = "";
9589  Display->GetOutputLog9()->Caption = "";
9590  Display->GetOutputLog10()->Caption = "";
9591  Utilities->CallLogPop(1352);
9592 }
9593 
9594 // ---------------------------------------------------------------------------
9595 
9597 {
9598  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9599  if(!TrainVector.empty())
9600  {
9601  for(unsigned int x = 0; x < TrainVector.size(); x++)
9602  {
9603  if(!TrainVectorAt(84, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
9604  { //see Kevin Smith error information for details
9605  TrainVectorAt(51, x).PlotTrain(4, Disp);
9606  }
9607  }
9608  }
9609  Utilities->CallLogPop(724);
9610 }
9611 
9612 // ---------------------------------------------------------------------------
9613 
9614 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9615 {
9616  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9617  if(!TrainVector.empty())
9618  {
9619  for(unsigned int x = 0; x < TrainVector.size(); x++)
9620  {
9621  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9622  }
9623  }
9624  Utilities->CallLogPop(1707);
9625 }
9626 
9627 // ---------------------------------------------------------------------------
9628 
9630 {
9631  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9632  if(!TrainVector.empty())
9633  {
9634  for(unsigned int x = 0; x < TrainVector.size(); x++)
9635  {
9636  TrainVectorAt(52, x).UnplotTrain(10);
9637  }
9638  }
9640  Utilities->CallLogPop(725);
9641 }
9642 
9643 // ---------------------------------------------------------------------------
9644 
9645 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9646  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9647  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9648 {
9649  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9650  "," + AnsiString(Mass) + "," + ModeStr);
9651  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9652  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr); //at v2.11.1 dropped later headcode - was listed twice
9653 
9654  int RearExitPos = -1;
9655 
9656  for(int x = 0; x < 4; x++)
9657  {
9658  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9659  {
9660  RearExitPos = x;
9661  }
9662  }
9663  if(RearExitPos == -1)
9664  {
9665  throw Exception("Error, RearExit == -1 in AddTrain");
9666  }
9667  bool ReportFlag = true;
9668 
9669  // used to stop repeated messages from CheckStartAllowable when split failed
9670  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9671  {
9672  ReportFlag = false;
9673  }
9674  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9675  {
9676  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9677  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9678  Utilities->CallLogPop(938);
9679  return(false);
9680  }
9681  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9682  TTrainMode TrainMode = NoMode;
9683 
9684  if(ModeStr == "Timetable")
9685  {
9686  TrainMode = Timetable;
9687  }
9688  // all else gives 'None', 'Signaller' set within program
9689 
9690  if(MaxRunningSpeed < 10)
9691  {
9692  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9693  }
9694  if(SignallerSpeed < 10)
9695  {
9696  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9697  }
9698  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9699  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9700 
9701  LogEvent("AddTrainSupplemental: Service Ref = " + TrainDataEntryPtr->ServiceReference + ", TrainID = " + AnsiString(NewTrain->TrainID)); //new at v2.11.1 so can relate headcode to ID
9702 
9703  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
9704  // initialise here rather than in TTrain constructor as create trains
9705  // with Null TrainDataEntryPtr when loading session trains
9706  if(SignallerControl)
9707  {
9708  NewTrain->TimetableFinished = true;
9709  NewTrain->SignallerStoppingFlag = false;
9710  NewTrain->TrainMode = Signaller;
9711  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
9712  {
9713  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
9714  }
9716  }
9717  // deal with starting conditions:-
9718  // unlocated Snt: just report entry & advance pointer
9719  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
9720  // Sns doesn't need a new train
9721  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
9722  // covers all above located starts
9723  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
9724  // wouldn't have accepted the timetable
9725  {
9726  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
9727  // StoppedAtBuffers is set in UpdateTrain()
9728  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
9729  // buffer end must be ahead of train or would have failed start position check
9730  {
9731  NewTrain->StoppedAtLocation = true;
9732  NewTrain->PlotStartPosition(0);
9734  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9735  NewTrain->ActionVectorEntryPtr->Warning);
9736  if(!SignallerControl) // don't advance if SignalControlEntry
9737  {
9738  NewTrain->ActionVectorEntryPtr++;
9739  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
9740  }
9741  NewTrain->LastActionTime = TTClockTime;
9742  }
9743  // else a through station stop
9744  else
9745  {
9746  NewTrain->StoppedAtLocation = true;
9747  NewTrain->PlotStartPosition(10);
9749  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9750  NewTrain->ActionVectorEntryPtr->Warning);
9751  if(!SignallerControl) // don't advance if SignalControlEntry
9752  {
9753  NewTrain->ActionVectorEntryPtr++;
9754  }
9755  NewTrain->LastActionTime = TTClockTime;
9756  }
9757  }
9758  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
9759  {
9760  NewTrain->PlotStartPosition(11);
9761  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
9762  AnsiString Loc = "";
9763  if(TE.ActiveTrackElementName != "")
9764  {
9765  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9766  }
9767  else
9768  {
9769  Loc = "track element " + TE.ElementID;
9770  }
9771  if(TE.TrackType == Continuation)
9772  {
9773  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9774  }
9775  else
9776  {
9777  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9778  }
9779  if(!SignallerControl) // don't advance if SignalControlEntry
9780  {
9781  NewTrain->ActionVectorEntryPtr++;
9782  }
9783  NewTrain->LastActionTime = TTClockTime;
9784  // no need to set LastActionTime for an unlocated entry
9785  }
9786  // cancel a wrong-direction route if either element of train starts on one
9787  if(NewTrain->LeadElement > -1)
9788  {
9789  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
9790  }
9791  if(NewTrain->MidElement > -1)
9792  {
9793  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
9794  }
9795  // set signals for a right-direction autosigs route for either element of train on one
9796  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
9797  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
9798  int RouteNumber = -1;
9799  bool SignalsSet = false;
9800 
9801  if(NewTrain->LeadElement > -1)
9802  {
9803  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9804  {
9805  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9806  int RouteStartPosition;
9807  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9808  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
9809  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
9810  if(FirstPair.first == RouteNumber)
9811  {
9812  RouteStartPosition = FirstPair.second;
9813  }
9814  else if(SecondPair.first == RouteNumber)
9815  {
9816  RouteStartPosition = SecondPair.second;
9817  }
9818  else
9819  {
9820  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
9821  }
9822  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
9823  SignalsSet = true;
9824  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9825  }
9826  else if(RouteNumber > -1) // non-autosigsroute
9827  {
9828  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
9829  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9830  int FirstELinkPos = TempPDE.GetELinkPos();
9831  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
9832  {
9833  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9834  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
9835  }
9836  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
9837  {
9838  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9839  // remove the last element under LeadElement
9840  }
9841  AllRoutes->RebuildRailwayFlag = true;
9842  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9843  // now deal with a rear linked autosigs route
9844  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
9845  {
9846  int LinkedRouteNumber = -1;
9847  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
9848  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9849  {
9850  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
9851  // this is ok as here we are setting signals from the start of the route
9852  }
9853  }
9854  SignalsSet = true;
9855  }
9856  }
9857  if(NewTrain->MidElement > -1)
9858  // if entering at a continuation MidElement == -1
9859  {
9860  // this is included in case a train starts with LeadElement on no route and MidElement on a route
9861  if(!SignalsSet)
9862  {
9863  RouteNumber = -1;
9864  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9865  {
9866  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9867  int RouteStartPosition;
9868  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9869  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
9870  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
9871  if(FirstPair.first == RouteNumber)
9872  {
9873  RouteStartPosition = FirstPair.second;
9874  }
9875  else if(SecondPair.first == RouteNumber)
9876  {
9877  RouteStartPosition = SecondPair.second;
9878  }
9879  else
9880  {
9881  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
9882  }
9883  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
9884  SignalsSet = true;
9885  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9886  }
9887  else if(RouteNumber > -1) // non-autosigsroute
9888  {
9889  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
9890  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9891  int FirstELinkPos = TempPDE.GetELinkPos();
9892  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
9893  {
9894  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9895  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
9896  }
9897  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
9898  {
9899  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9900  // remove the last element under LeadElement
9901  }
9902  AllRoutes->RebuildRailwayFlag = true;
9903  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9904  // now deal with a rear linked autosigs route
9905  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
9906  {
9907  int LinkedRouteNumber = -1;
9908  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
9909  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9910  {
9911  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
9912  // this is ok as now we are setting signals from the start of the route
9913  }
9914  }
9915  }
9916  }
9917  }
9918  TrainVector.push_back(*NewTrain);
9919  Utilities->CallLogPop(731);
9920  return(true);
9921 }
9922 
9923 // ---------------------------------------------------------------------------
9924 
9925 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
9926 {
9927  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
9928  AnsiString(TrackVectorNumber));
9929  int VecPos = -1;
9930 
9931  for(unsigned int x = 0; x < TrainVector.size(); x++)
9932  {
9933  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
9934  {
9935  VecPos = x;
9936  }
9937  }
9938  if(VecPos == -1)
9939  {
9940  throw Exception("Error, VecPos not set in EntryPos");
9941  }
9942  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
9943  {
9944  Utilities->CallLogPop(734);
9945  return(TrainVectorAt(3, VecPos).LeadEntryPos);
9946  }
9947  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
9948  {
9949  Utilities->CallLogPop(735);
9950  return(TrainVectorAt(5, VecPos).MidEntryPos);
9951  }
9952  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
9953  {
9954  Utilities->CallLogPop(736);
9955  return(TrainVectorAt(7, VecPos).LagEntryPos);
9956  }
9957  Utilities->CallLogPop(737);
9958  return(-1);
9959 }
9960 
9961 // ---------------------------------------------------------------------------
9962 
9964 {
9965  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
9966  for(unsigned int x = 0; x < TrainVector.size(); x++)
9967  {
9968  if(TrainVectorAt(53, x).TrainID == TrainID)
9969  {
9970  Utilities->CallLogPop(738);
9971  return(TrainVectorAt(54, x));
9972  }
9973  }
9974  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
9975 }
9976 
9977 // ---------------------------------------------------------------------------
9978 
9979 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
9980 // return true if find the train (added at v2.4.0 as can select a removed train
9981 // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
9982 {
9983  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
9984  for(unsigned int x = 0; x < TrainVector.size(); x++)
9985  {
9986  if(TrainVectorAt(69, x).TrainID == TrainID)
9987  {
9988  Utilities->CallLogPop(2152);
9989  return(true);
9990  }
9991  }
9992  Utilities->CallLogPop(2153);
9993  return(false);
9994 }
9995 
9996 // ---------------------------------------------------------------------------
9997 
9998 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
9999 {
10000  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
10001  Utilities->Format96HHMMSS(Time));
10002  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
10003 
10004  Utilities->CallLogPop(2061);
10005  return(RepeatTime);
10006 }
10007 
10008 // ---------------------------------------------------------------------------
10009 
10010 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
10011 // Enter with Ptr pointing to first action to be listed (i.e. next action)
10012 {
10013  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
10014  AnsiString RetStr = "", PartStr = "";
10015  int Count = 0;
10016  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
10017 
10018  Ptr--; // because incremented at start of loop
10019  do
10020  {
10021  Ptr++;
10022  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
10023  {
10024  continue; // move past the starting entry
10025  }
10026  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
10027  {
10028  break;
10029  }
10030  if(Ptr->SignallerControl)
10031  {
10032  RetStr = "Train under signaller control";
10033  break;
10034  }
10035  if(Ptr->FormatType == TimeTimeLoc)
10036  {
10037  if(Ptr->ArrivalTime == Ptr->DepartureTime)
10038  {
10039  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
10040  }
10041  else
10042  {
10043  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
10044  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10045  Count++; // because there are 2 entries
10046  }
10047  }
10048  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
10049  {
10050  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
10051  }
10052  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
10053  {
10054  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10055  }
10056  else if(Ptr->FormatType == PassTime) // new
10057  {
10058  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
10059  }
10060  else if(Ptr->Command == "Fns")
10061  {
10062  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10063  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10064  PartStr = ControllerGetNewServiceDepartureInfo(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
10065  }
10066  else if(Ptr->Command == "F-nshs")
10067  {
10068  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10069  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
10070  PartStr = ControllerGetNewServiceDepartureInfo(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10071  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
10072  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
10073  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
10074  }
10075 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
10076  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10077  {
10078  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10079  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10080  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10081  PartStr = ControllerGetNewServiceDepartureInfo(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10082  }
10083  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10084  {
10085  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10086  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
10087  PartStr = ControllerGetNewServiceDepartureInfo(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10088  }
10089  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10090  {
10091  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10092  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10093  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10094  PartStr = ControllerGetNewServiceDepartureInfo(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10095  }
10096  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10097  {
10098  PartStr = "Terminate at " + Ptr->LocationName;
10099  }
10100  else if(Ptr->Command == "Frh")
10101  {
10102  PartStr = "Terminate at " + Ptr->LocationName;
10103  }
10104  else if(Ptr->Command == "Fer")
10105  {
10106  AnsiString AllowedExits;
10107  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
10108  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
10109  }
10110  else if(Ptr->Command == "Fjo")
10111  {
10112  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
10113  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10114  }
10115  else if(Ptr->Command == "jbo")
10116  {
10117  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
10118  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10119  }
10120  else if(Ptr->Command == "fsp")
10121  {
10122  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
10123  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10124  }
10125  else if(Ptr->Command == "rsp")
10126  {
10127  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
10128  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10129  }
10130  else if(Ptr->Command == "cdt")
10131  {
10132  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
10133  }
10134  if(RetStr != "")
10135  {
10136  RetStr = RetStr + '\n' + PartStr;
10137  }
10138  else
10139  {
10140  RetStr = PartStr;
10141  }
10142  Count++;
10143  }
10144  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
10145  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
10146  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
10147  // forward as anyone should wish to see without looking at the full timetable
10148  Utilities->CallLogPop(2072);
10149  return(RetStr);
10150 }
10151 
10152 // ---------------------------------------------------------------------------
10153 
10154 AnsiString TTrainController::ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
10155 {
10156  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
10157  + AnsiString(RptNum) + ",ControllerGetNewServiceDepartureInfo," + TDEPtr->HeadCode);
10158  AnsiString DepTime = "", EventTime = "";
10159  bool CDTFlag = false; //reports if train changes direction before departs
10160  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
10161  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
10162  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
10163  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
10164  {
10165  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
10166  {
10167  TowardsLocation = AVI->LocationName;
10168  }
10169  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
10170  {
10171  TTrackElement TE = Track->TrackElementAt(1453, (AVI->ExitList.front()));
10172  if(TE.ActiveTrackElementName != "")
10173  {
10174  TowardsLocation = TE.ActiveTrackElementName;
10175  }
10176  else
10177  {
10178  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
10179  }
10180  }
10181  }
10182  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
10183  {
10184  if(AVI->Command == "cdt")
10185  {
10186  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
10187  continue;
10188  }
10189  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
10190  {
10191  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
10192  RetStr += "\nNew service splits at " + EventTime;
10193  Utilities->CallLogPop(2237);
10194  return(RetStr);
10195  }
10196  if(AVI->Command == "jbo")
10197  {
10198  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
10199  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
10200  Utilities->CallLogPop(2238);
10201  return(RetStr);
10202  }
10203  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
10204  {
10205  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
10206  if(CDTFlag)
10207  {
10208  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10209  {
10210  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
10211  }
10212  else
10213  {
10214  RetStr += "\nNew service changes direction then departs at " + DepTime;
10215  }
10216  }
10217  else
10218  {
10219  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10220  {
10221  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
10222  }
10223  else
10224  {
10225  RetStr += "\nNew service departs at " + DepTime;
10226  }
10227  }
10228  Utilities->CallLogPop(2239);
10229  return(RetStr);
10230  }
10231  }
10232  Utilities->CallLogPop(2223);
10233  return(RetStr);
10234 }
10235 
10236 // ---------------------------------------------------------------------------
10237 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
10238 /*
10239  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
10240  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
10241  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
10242 
10243  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
10244  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
10245  user wishes
10246 
10247  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10248  descriptive text or anything user wishes
10249  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10250  be ignored) is taken as the timetable start time.
10251  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10252  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10253  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10254  within the timetable if required.
10255  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10256  services)
10257  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10258  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10259 
10260  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10261  text timetable file easier
10262 
10263  form:-
10264  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10265  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10266  then multiple entries, separated by commas, of the form:-
10267 
10268  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10269  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
10270  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
10271 
10272  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
10273  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
10274  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
10275 
10276  HH:MM;Command (cdt) }TimeCmd }
10277  HH:MM;Location (arr & dep) }TimeLoc }
10278  HH:MM;HH:MM;Location }TimeTimeLoc }
10279  HH:MM;pas;Location }PassTime }
10280  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
10281  HH:MM;Fer;set of allowable IDs }ExitRailway }
10282  Command (Frh only) }FinRemHere }
10283 
10284  R;mm;dd;nn. Repeat Repeat entry
10285 
10286  Formats:
10287 
10288  Command only: Frh
10289  Time;Command: cdt
10290  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
10291  Time;Command;2 Element IDs: Snt
10292  Time;Comand;n Element IDs: Fer
10293  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
10294  Time;Command;2 Element IDs;Headcode Snt-sh
10295  Time;Command;Location pas
10296  Time;Location Arr Dep
10297  Time;Time;Location Arr & dep together
10298 
10299  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
10300 
10301  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
10302  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
10303  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
10304 
10305  4 2x Linked entries, all shuttles:
10306 
10307  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
10308  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
10309  -> Remain Here (at finish location after all repeats)
10310  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
10311  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
10312 
10313  Allowable successors:-
10314 
10315  Successor state Type
10316 
10317  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
10318  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
10319  Sfs AtLoc )
10320  Sns AtLoc ) Start
10321  Sns-fsh AtLoc )
10322  Snt-sh AtLoc )
10323  Sns-sh AtLoc )
10324 
10325  pas Moving )
10326  jbo AtLoc )
10327  fsp AtLoc )
10328  rsp AtLoc ) Intermediate
10329  cdt AtLoc )
10330  TimeLoc arr Moving (bef), AtLoc (aft) )
10331  TimeLoc dep AtLoc (bef), Moving (aft) )
10332  TimeTimeLoc Moving )
10333 
10334  Fns Repeat/Nothing)
10335  Fjo Repeat/Nothing)
10336  Frh Repeat/Nothing)
10337  Fer Repeat/Nothing) Finish
10338  Frh-sh Repeat )
10339  Fns-sh Repeat )
10340  F-nshs Nothing )
10341 
10342  Descriptions:
10343  Snt New train
10344  Sfs New service from split
10345  Sns New service from another service
10346  Sns-fsh New non-repeating service from a shuttle service
10347  Snt-sh New shuttle train at a timetabled stop
10348  Sns-sh New shuttle service from a feeder service
10349 
10350  pas Pass
10351  jbo Be joined by another train
10352  fsp Front split
10353  rsp Rear split
10354  cdt Change direction of train
10355  TimeLoc arr Arrival
10356  TimeLoc dep Departure
10357  TimeTimeLoc Arrival and departure
10358 
10359  Fns Finish & form a new service
10360  Fjo Finish & join another train
10361  Frh Finish & remain here
10362  Fer Finish & exit railway
10363  Frh-sh Finish & repeat shuttle, finally remain here
10364  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
10365  F-nshs Finish & form a shuttle feeder service
10366 */
10367 
10368 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
10369 {
10370  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
10371  // a line that is too long; timetable containing too few lines; and timetable failed to open.
10372  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
10373  // new for v0.2b
10374  // compile ActiveTrackElementNameMap
10375  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
10376 
10378  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
10379  {
10380  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
10382  == Track->ContinuationNameMap.end())
10383  {
10384  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
10385  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
10386  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
10387  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
10388  }
10389  }
10391  // end of new section
10392  std::ifstream TTBLFile(FileName, std::ios_base::binary);
10393 
10394  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
10395  if(TTBLFile.is_open())
10396  {
10397  char *TrainTimetableString = new char[10000];
10398  // enough for over 200 stations, should be adequate!
10399  bool EndOfFile = false;
10400  int Count = 0;
10401  // counts 'relevant' lines, i.e ignores any before the start time on its own line
10402  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10403  // delimiter is '\0' as it's an AnsiString
10404  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10405  // file empty - stores a null in 1st position if doesn't load any characters
10406  {
10407  // may still have eof even if read a line (no CRLF at end), and
10408  // if so need to process it
10409  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
10410  TTBLFile.close();
10411  delete[] TrainTimetableString;
10412  Utilities->CallLogPop(1611);
10413  return(false);
10414  }
10415  AnsiString OneLine(TrainTimetableString);
10416  bool FinalCallFalse = false;
10417  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10418  // get rid of lines before the start time
10419  {
10420  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
10421  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10422  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10423  // stores a null in 1st position if doesn't load any characters
10424  {
10425  // may still have eof even if read a line (no CRLF at end), and
10426  // if so need to process it
10427  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
10428  TTBLFile.close();
10429  delete[] TrainTimetableString;
10430  Utilities->CallLogPop(772);
10431  return(false);
10432  }
10433  OneLine = AnsiString(TrainTimetableString);
10434  }
10435  // here when have accepted the start time
10436  Count++; // increment past the start time
10437  while(!EndOfFile)
10438  {
10439  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10440  // get next line after start time
10441  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10442  // stores a null in 1st position if doesn't load any characters
10443  {
10444  // may still have eof even if read a line (no CRLF at end), and
10445  // if so need to process it
10446  EndOfFile = true;
10447  OneLine = "";
10448  }
10449  else
10450  {
10451  OneLine = AnsiString(TrainTimetableString);
10452  }
10453  if(OneLine.Length() > 9999)
10454  {
10455  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
10456  TTBLFile.close();
10457  delete[] TrainTimetableString;
10458  Utilities->CallLogPop(789);
10459  return(false);
10460  }
10461  bool FinalCallFalse = false;
10462  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10463  // false for FinalCall - just checking at this stage
10464  {
10465  TTBLFile.close();
10466  delete[] TrainTimetableString;
10467  Utilities->CallLogPop(770);
10468  return(false);
10469  }
10470  if(EndOfFile && (Count < 2))
10471  // Timetable must contain at least two relevant lines, one for start time and at least one train
10472  {
10473  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
10474  TTBLFile.close();
10475  delete[] TrainTimetableString;
10476  Utilities->CallLogPop(771);
10477  return(false);
10478  }
10479  Count++;
10480  }
10481  delete[] TrainTimetableString;
10482  TTBLFile.close();
10483  } // if(TTBLFile.is_open())
10484  else
10485  {
10486  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
10487  Utilities->CallLogPop(2154);
10488  return(false);
10489  }
10490  Utilities->CallLogPop(753);
10491  return(true);
10492 }
10493 
10494 // ---------------------------------------------------------------------------
10495 
10496 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
10497  bool CheckLocationsExistInRailway) // return true for success
10498 
10499 /* Format:
10500  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10501  descriptive text or anything user wishes
10502  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10503  be ignored) is taken as the timetable start time.
10504  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10505  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10506  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10507  within the timetable if required.
10508  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10509  services)
10510  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10511  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10512 
10513  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10514  text timetable file easier
10515 
10516  form:-
10517  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10518  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10519  then multiple entries, separated by commas, of the form:-
10520 
10521  Format FormatType
10522  [W]HH:MM;Command (cdt) }TimeCmd }
10523  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
10524  [W]HH:MM;pas;Location }PassTime }
10525  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10526  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
10527  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
10528  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
10529  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
10530  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
10531  [W]HH:MM;Fns-sh;Details }FSHNewService }
10532  [W]HH:MM;Location (arr & dep) }TimeLoc }
10533  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
10534  Command (Frh only) }FinRemHere }
10535 
10536  R;mm;dd;nn. Repeat Repeat entry
10537 
10538  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
10539  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
10540  at location; or (c) departure time if train already at location (including train started at location either as a new
10541  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
10542  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
10543  minutes, incremental train headcode last 2 digits, and number of repeats.
10544 
10545  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
10546  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
10547  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
10548  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
10549  (it's for a shuttle train to return to depot at end of services)
10550 
10551  Command/Location & details are as follows:-
10552 
10553  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
10554  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
10555  2E44 in its Sfs entry. All these are checked.
10556  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
10557 
10558  Start commands:-
10559  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
10560  with loc as a start entry can't have a location as details)
10561  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
10562  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
10563  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
10564  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
10565  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
10566  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
10567  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
10568  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
10569  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
10570  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
10571  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
10572 
10573  Intermediate commands:-
10574  Time - Location (TimeLoc), can be arrival or departure depending on context
10575  Time Time location (TimeTimeLoc), arrival and departure
10576  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
10577  pas (PassTime), Time;pas;Location
10578  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
10579  joining train's finish details must correspond or the file check will fail
10580  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
10581  new train - that train's starting information must correspond)
10582  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
10583  new train - that train's starting information must correspond)
10584  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
10585 
10586  Finish commands:-
10587  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
10588  creation)
10589  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
10590  shuttle headcode (no train creation)
10591  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
10592  may have to wait for it), details = new headcode (delete train)
10593  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
10594  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
10595  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
10596  here
10597  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
10598  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
10599 
10600  Repeat:-
10601  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
10602  headcodes - it is up to user to avoid duplicates if he/she wishes to.
10603 
10604  Checks carried out with error messages in this function:-
10605  At least one comma in a service line (it's based on a .csv file)
10606  No entries following train information;
10607  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10608  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10609  First entry not a start entry;
10610  Train information incomplete before a start entry;
10611  Entry follows a finish entry but doesn't begin with 'R';
10612  SplitEntry returns false in a finish entry - message repeats the entry for information;
10613  Last action entry isn't a finish entry.
10614 
10615  Function returns false with no message if:-
10616  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10617  time is found at all then an error message is given in the calling function);
10618  SplitTrainInfo returns false (message given in called function);
10619  SplitRepeat returns false (message given in called function).
10620 */{
10621  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
10622  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
10623  TTrainDataEntry TempTrainDataEntry;
10624 
10625  EndOfFile = false;
10626  StripSpaces(0, OneLine);
10627  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10628  // semicolons within the line
10629  ServiceReference = "";
10630  if(OneLine != "")
10631  {
10632  if(OneLine[1] != '*')
10633  {
10634  int SCPos = OneLine.Pos(';');
10635  if(SCPos == 0)
10636  {
10637  ServiceReference = OneLine.SubString(1, 8);
10638  }
10639  else
10640  {
10641  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10642  }
10643  }
10644  }
10645  bool AllCommas = true;
10646 
10647  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10648  {
10649  if(OneLine[x] != ',')
10650  {
10651  AllCommas = false;
10652  }
10653  }
10654  if(AllCommas || (OneLine == ""))
10655  {
10656  if(Count > 0)
10657  {
10658  EndOfFile = true;
10659  // returns true for a blank line - treated as end of file
10660  Utilities->CallLogPop(1018);
10661  return(true);
10662  }
10663  else // count == 0 so not yet found a start time, no message to be given
10664  {
10665  Utilities->CallLogPop(754);
10666  return(false);
10667  }
10668  }
10669  AnsiString First = "", Second = "", Third = "", Fourth = "";
10670  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10671  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10672  TDateTime StartTime(0);
10673  TNumList ExitList;
10674  bool Warning = false;
10675 
10676  if(Count == 0) // no start time found yet
10677  {
10678 /* dropped at v0.6b
10679  AnyHeadCodeValid = false;
10680  if(OneLine.SubString(6,5) == ";0000")
10681  {
10682  AnyHeadCodeValid = true;
10683  }
10684 */
10685  if(!CheckTimeValidity(0, OneLine, StartTime))
10686  {
10687  // no message is given for an invalid time as it's assumed to be an irrelevant line
10688  // if no start time is found at all then an error message is given in the calling function
10689  // AnyHeadCodeValid = false;
10690  Utilities->CallLogPop(755);
10691  return(false);
10692  }
10693  if(FinalCall) // here if start time valid
10694  {
10695  TTClockTime = StartTime;
10696  TimetableStartTime = StartTime;
10697  }
10698  }
10699  else
10700  {
10701  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10702  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
10703  double MaxBrakeRate = 0;
10704  double PowerAtRail = 0;
10705  int SignallerSpeed = 0;
10706  if(OneLine[1] == '*')
10707  {
10708  Utilities->CallLogPop(1581);
10709  return(true);
10710  // ignore any line beginning with '*' but return true as there is no error
10711  }
10712  int Pos = OneLine.Pos(',');
10713  if(Pos == 0)
10714  {
10715  int SubStringLength = 20;
10716  if(OneLine.Length() < 20)
10717  {
10718  SubStringLength = OneLine.Length();
10719  }
10720  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
10721  Utilities->CallLogPop(766);
10722  return(false);
10723  }
10724  TrainInfoStr = OneLine.SubString(1, Pos - 1);
10725  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
10726  GiveMessages)) // error messages given in SplitTrainInfo
10727  {
10728  Utilities->CallLogPop(773);
10729  return(false);
10730  }
10731  if(FinalCall)
10732  {
10733  // store Train info - conversions done in SplitTrainInfo
10734  // only headcode mandatory for continuing services
10735  TempTrainDataEntry.HeadCode = HeadCode;
10736  TempTrainDataEntry.ServiceReference = HeadCode;
10737  TempTrainDataEntry.Description = Description;
10738  TempTrainDataEntry.StartSpeed = StartSpeed;
10739  TempTrainDataEntry.Mass = Mass;
10740  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
10741  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
10742  TempTrainDataEntry.PowerAtRail = PowerAtRail;
10743  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
10744  TTrainOperatingData TempTrainOperatingData;
10745  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
10746  }
10747  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
10748  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
10749  // so strip them off
10750  while(NewRemainder[NewRemainder.Length()] == ',')
10751  {
10752  if(NewRemainder.Length() > 1)
10753  {
10754  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
10755  }
10756  else
10757  {
10758  NewRemainder = "";
10759  break;
10760  }
10761  }
10762  // check if zero length & fail if so
10763  if(NewRemainder == "")
10764  {
10765  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
10766  Utilities->CallLogPop(769);
10767  return(false);
10768  }
10769  // now have one more entry than there are commas
10770  int CommaCount = 0;
10771  for(int x = 1; x < NewRemainder.Length() + 1; x++)
10772  {
10773  if(NewRemainder[x] == ',')
10774  {
10775  CommaCount++;
10776  }
10777  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
10778  if(CommaCount == 0)
10779  {
10780  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
10781  {
10782  int SubStringLength = 20;
10783  if(OneLine.Length() < 20)
10784  {
10785  SubStringLength = OneLine.Length();
10786  }
10787  TimetableMessage(GiveMessages,
10788  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
10789  OneLine.SubString(1, SubStringLength) + "'....");
10790  Utilities->CallLogPop(783);
10791  return(false);
10792  }
10793  }
10794  AnsiString OneEntry = "";
10795  TTimetableFormatType FormatType;
10796  TTimetableSequenceType SequenceType;
10797  TTimetableLocationType LocationType;
10798  TTimetableShuttleLinkType ShuttleLinkType;
10799  bool FinishFlag = false;
10800  for(int x = 0; x < CommaCount + 1; x++)
10801  {
10802  if((CommaCount == 0) || (x < CommaCount))
10803  // i.e. train entered under signaller control with no repeats, or entry is not the last,
10804  // in which case there's a comma & finish element or repeat still to come this entry could
10805  // be a finish but can't be a repeat
10806  {
10807  if(CommaCount == 0)
10808  {
10809  OneEntry = NewRemainder;
10810  NewRemainder = "";
10811  }
10812  else
10813  {
10814  Pos = NewRemainder.Pos(',');
10815  OneEntry = NewRemainder.SubString(1, Pos - 1);
10816  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
10817  }
10818  First = "";
10819  Second = "";
10820  Third = "";
10821  Fourth = "";
10822  RearStartOrRepeatMins = 0;
10823  FrontStartOrRepeatDigits = 0;
10824  NumberOfRepeats = 0;
10825  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10826  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10827  {
10828  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10829  Utilities->CallLogPop(756);
10830  return(false);
10831  }
10832  // check if warning for Frh or Fjo & reject
10833  if(Warning && (Second == "Frh"))
10834  {
10835  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
10836  Utilities->CallLogPop(1793);
10837  return(false);
10838  }
10839  if(Warning && (Second == "Fjo"))
10840  {
10841  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
10842  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
10843  Utilities->CallLogPop(1794);
10844  return(false);
10845  }
10846  if(x == 0) // should be start event
10847  {
10848  if(SequenceType != Start)
10849  {
10850  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
10851  Utilities->CallLogPop(784);
10852  return(false);
10853  }
10854  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
10855  {
10856  if(NewRemainder[1] != 'R')
10857  {
10858  TimetableMessage(GiveMessages,
10859  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
10860  OneEntry + "'");
10861  Utilities->CallLogPop(787);
10862  return(false);
10863  }
10864  }
10865  if((Second == "Snt") || (Second == "Snt-sh"))
10866  // need full train information including non-default values for at least HeadCode, Description,
10867  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
10868  {
10869  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
10870  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
10871  {
10872  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
10873  OneEntry + "'");
10874  Utilities->CallLogPop(1783);
10875  return(false);
10876  }
10877  }
10878  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
10879  // service continuation - need at least non-default value for HeadCode
10880  {
10881  if(HeadCode == "")
10882  {
10883  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10884  OneEntry + "'");
10885  Utilities->CallLogPop(788);
10886  return(false);
10887  }
10888  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
10889  {
10890  TimetableMessage(GiveMessages,
10891  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10892  OneEntry + "'");
10893  Utilities->CallLogPop(843);
10894  return(false);
10895  }
10896  }
10897  }
10898  if(SequenceType == Finish)
10899  {
10900  FinishFlag = true;
10901  // marker for only permitted additional entry being a repeat, only needed if the
10902  // finish entry is not the last entry
10903  }
10904  if(FinalCall)
10905  {
10906  // interpret & add to ActionVector
10907  TDateTime TempTime;
10908  TActionVectorEntry ActionVectorEntry;
10909  ActionVectorEntry.FormatType = FormatType;
10910  ActionVectorEntry.LocationType = LocationType;
10911  ActionVectorEntry.SequenceType = SequenceType;
10912  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10913  ActionVectorEntry.Warning = Warning;
10914  if(FormatType == TimeLoc)
10915  {
10916  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
10917  {
10918  ;
10919  } // these will all be true as final call
10920 
10921  ActionVectorEntry.LocationName = Second;
10922  }
10923  else if(FormatType == PassTime) // new
10924  {
10925  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
10926  {
10927  ;
10928  }
10929  ActionVectorEntry.Command = Second;
10930  ActionVectorEntry.LocationName = Third;
10931  }
10932  else if(FormatType == TimeTimeLoc)
10933  {
10934  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
10935  {
10936  ;
10937  }
10938  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
10939  {
10940  ;
10941  }
10942  ActionVectorEntry.LocationName = Third;
10943  }
10944  else if(FormatType == TimeCmd)
10945  {
10946  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
10947  {
10948  ;
10949  }
10950  ActionVectorEntry.Command = Second;
10951  }
10952  else if(FormatType == ExitRailway)
10953  {
10954  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
10955  {
10956  ;
10957  }
10958  ActionVectorEntry.Command = Second;
10959  ActionVectorEntry.ExitList = ExitList;
10960  }
10961  else if(FormatType == StartNew)
10962  {
10963  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
10964  {
10965  ;
10966  }
10967  ActionVectorEntry.Command = Second;
10968  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10969  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10970  if(Fourth == 'S')
10971  {
10972  ActionVectorEntry.SignallerControl = true;
10973  }
10974  }
10975  else if(FormatType == SNTShuttle)
10976  {
10977  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
10978  {
10979  ;
10980  }
10981  ActionVectorEntry.Command = Second;
10982  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10983  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10984  ActionVectorEntry.OtherHeadCode = Fourth;
10985  }
10986  else if(FormatType == SNSShuttle)
10987  {
10988  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
10989  {
10990  ;
10991  }
10992  ActionVectorEntry.Command = Second;
10993  ActionVectorEntry.OtherHeadCode = Third;
10994  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10995  }
10996  else if(FormatType == TimeCmdHeadCode)
10997  {
10998  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
10999  {
11000  ;
11001  }
11002  ActionVectorEntry.Command = Second;
11003  ActionVectorEntry.OtherHeadCode = Third;
11004  }
11005  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
11006  {
11007  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
11008  {
11009  ;
11010  }
11011  ActionVectorEntry.Command = Second;
11012  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11013  }
11014  else if(FormatType == FSHNewService)
11015  {
11016  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
11017  {
11018  ;
11019  }
11020  ActionVectorEntry.Command = Second;
11021  ActionVectorEntry.OtherHeadCode = Third;
11022  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11023  }
11024  else if(FormatType == FinRemHere)
11025  {
11026  ActionVectorEntry.Command = Second;
11027  }
11028  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11029  }
11030  }
11031  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
11032  {
11033  OneEntry = NewRemainder;
11034  First = "";
11035  Second = "";
11036  Third = "";
11037  Fourth = "";
11038  RearStartOrRepeatMins = 0;
11039  FrontStartOrRepeatDigits = 0;
11040  NumberOfRepeats = 0;
11041  if((FinishFlag) && (OneEntry[1] != 'R'))
11042  // already had a finish entry
11043  {
11044  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
11045  Utilities->CallLogPop(79);
11046  return(false);
11047  }
11048  if(OneEntry[1] != 'R') // must be finish
11049  {
11050  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11051  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11052  {
11053  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11054  Utilities->CallLogPop(757);
11055  return(false);
11056  }
11057  if(SequenceType != Finish)
11058  {
11059  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
11060  Utilities->CallLogPop(785);
11061  return(false);
11062  }
11063  if(FinalCall)
11064  {
11065  // interpret & add to ActionVector
11066  TDateTime TempTime;
11067  TActionVectorEntry ActionVectorEntry;
11068  ActionVectorEntry.FormatType = FormatType;
11069  ActionVectorEntry.LocationType = LocationType;
11070  ActionVectorEntry.SequenceType = SequenceType;
11071  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11072  ActionVectorEntry.Warning = Warning;
11073  if(FormatType == TimeCmd)
11074  {
11075  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
11076  {
11077  ;
11078  }
11079  ActionVectorEntry.Command = Second;
11080  }
11081  else if(FormatType == TimeCmdHeadCode)
11082  {
11083  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
11084  {
11085  ;
11086  }
11087  ActionVectorEntry.Command = Second;
11088  ActionVectorEntry.OtherHeadCode = Third;
11089  }
11090  else if(FormatType == FNSNonRepeatToShuttle)
11091  {
11092  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
11093  {
11094  ;
11095  }
11096  ActionVectorEntry.Command = Second;
11097  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11098  }
11099  else if(FormatType == FSHNewService)
11100  {
11101  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
11102  {
11103  ;
11104  }
11105  ActionVectorEntry.Command = Second;
11106  ActionVectorEntry.OtherHeadCode = Third;
11107  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11108  }
11109  else if(FormatType == ExitRailway)
11110  {
11111  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
11112  {
11113  ;
11114  }
11115  ActionVectorEntry.Command = Second;
11116  ActionVectorEntry.ExitList = ExitList;
11117  }
11118  else if(FormatType == FinRemHere)
11119  {
11120  ActionVectorEntry.Command = Second;
11121  }
11122  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11123  }
11124  }
11125  else // repeat
11126  {
11127  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
11128  {
11129  Utilities->CallLogPop(786);
11130  // error messages given in SplitRepeat
11131  return(false);
11132  }
11133  if(FinalCall)
11134  {
11135  TActionVectorEntry ActionVectorEntry;
11136  ActionVectorEntry.FormatType = Repeat;
11137  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
11138  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
11139  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
11140  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11141  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11142  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
11143  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11144  }
11145  }
11146  }
11147  }
11148  if(FinalCall)
11149  {
11150  TrainDataVector.push_back(TempTrainDataEntry);
11151  }
11152  }
11153  Utilities->CallLogPop(80);
11154  return(true);
11155 }
11156 
11157 // ---------------------------------------------------------------------------
11158 
11159 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
11160 {
11161  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
11162  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
11163  {
11164  Utilities->CallLogPop(1890);
11165  return(false);
11166  }
11167  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
11168  {
11169  Utilities->CallLogPop(1891);
11170  return(false);
11171  }
11172  Utilities->CallLogPop(1892);
11173  return(true);
11174 }
11175 
11176 // ---------------------------------------------------------------------------
11177 
11178 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
11179 // 1st 5 chars must be HH:MM, anything else will be ignored
11180 {
11181  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
11182  if(TimeStr.Length() < 5)
11183  {
11184  Utilities->CallLogPop(926);
11185  return(false);
11186  }
11187  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
11188  {
11189  Utilities->CallLogPop(927);
11190  return(false);
11191  }
11192  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
11193  {
11194  Utilities->CallLogPop(928);
11195  return(false);
11196  }
11197  if(TimeStr[3] != ':')
11198  {
11199  Utilities->CallLogPop(929);
11200  return(false);
11201  }
11202  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
11203  {
11204  Utilities->CallLogPop(930);
11205  return(false);
11206  }
11207  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
11208  {
11209  Utilities->CallLogPop(931);
11210  return(false);
11211  }
11212  while(TimeStr.Length() > 5)
11213  {
11214  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
11215  }
11216  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
11217  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
11218 
11219  if((WholeHours + FracHour) >= 95.98334)
11220  {
11221  Utilities->CallLogPop(1817);
11222  return(false); // > 95h 59m
11223  }
11224  Time = TDateTime((WholeHours + FracHour) / 24);
11225  Utilities->CallLogPop(932);
11226  return(true);
11227 }
11228 
11229 // ---------------------------------------------------------------------------
11230 
11231 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
11232  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
11233  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
11234 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
11235  Return false for failure.
11236  See description above under ProcessOneTimetableLinefor details of train action entries
11237  NB all types set except LocationType for Sns as may be located or not
11238 */{
11239  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
11240  Warning = false;
11241  TDateTime TempTime;
11242 
11243  if(OneEntry.Length() > 0)
11244  {
11245  if(OneEntry[1] == 'W') // warning
11246  {
11247  Warning = true;
11248  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
11249  // strip it off
11250  }
11251  }
11252  if(OneEntry == "Frh")
11253  {
11254  FormatType = FinRemHere;
11255  SequenceType = Finish;
11256  LocationType = AtLocation;
11257  ShuttleLinkType = NotAShuttleLink;
11258  Second = "Frh";
11259  Utilities->CallLogPop(1016);
11260  return(true);
11261  }
11262  if(OneEntry.Length() < 7)
11263  {
11264  Utilities->CallLogPop(907);
11265  return(false); // 'HH:MM;' + at least a one-letter location name
11266  }
11267  int Pos = OneEntry.Pos(';'); // first segment delimiter
11268 
11269  if(Pos != 6)
11270  {
11271  Utilities->CallLogPop(908);
11272  return(false);
11273  // no delimiter or delimiter not in position 6, has to be a time so fail
11274  }
11275  First = OneEntry.SubString(1, 5); // has to be a time
11276  if(!CheckTimeValidity(16, First, TempTime))
11277  {
11278  Utilities->CallLogPop(909);
11279  return(false);
11280  }
11281  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
11282 
11283  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
11284  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
11285  {
11286  if(Remainder.Length() < 7)
11287  {
11288  Utilities->CallLogPop(910);
11289  return(false); // 'HH:MM;' + at least a one-letter location name
11290  }
11291  Pos = Remainder.Pos(';'); // second segment delimiter
11292  if(Pos == 0)
11293  {
11294  Utilities->CallLogPop(911);
11295  return(false);
11296  // no delimiter, has to be one between departure time & location
11297  }
11298  Second = Remainder.SubString(1, 5); // has to be a time
11299  if(!CheckTimeValidity(15, Second, TempTime))
11300  {
11301  Utilities->CallLogPop(912);
11302  return(false);
11303  }
11304  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11305  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
11306  {
11307  Utilities->CallLogPop(913);
11308  return(false);
11309  }
11310  FormatType = TimeTimeLoc;
11311  SequenceType = Intermediate;
11312  LocationType = AtLocation;
11313  ShuttleLinkType = NotAShuttleLink;
11314  Utilities->CallLogPop(914);
11315  return(true);
11316  }
11317  Pos = Remainder.Pos(';'); // second segment delimiter
11318  if(Pos == 0) // no third segment so second must be a location, or cdt
11319  {
11320  Second = Remainder;
11321  if(Second == "cdt")
11322  {
11323  FormatType = TimeCmd;
11324  ShuttleLinkType = NotAShuttleLink;
11325  LocationType = AtLocation;
11326  SequenceType = Intermediate;
11327  Utilities->CallLogPop(915);
11328  return(true);
11329  }
11330  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
11331  {
11332  Utilities->CallLogPop(916);
11333  return(false);
11334  }
11335  else
11336  {
11337  FormatType = TimeLoc;
11338  LocationType = AtLocation;
11339  SequenceType = Intermediate;
11340  ShuttleLinkType = NotAShuttleLink;
11341  Utilities->CallLogPop(917);
11342  return(true);
11343  }
11344  }
11345  // here if second segment is a command, with a third & maybe fourth segments as details
11346  if((Pos != 4) && (Pos != 7) && (Pos != 8))
11347  {
11348  Utilities->CallLogPop(918);
11349  return(false);
11350  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
11351  }
11352  Second = Remainder.SubString(1, Pos - 1); // command
11353 
11354  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11355  // details
11356  Pos = Remainder.Pos(';'); // third segment delimiter
11357  if(Pos == 0)
11358  {
11359  Third = Remainder;
11360  }
11361  else
11362  {
11363  Third = Remainder.SubString(1, Pos - 1);
11364  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11365  }
11366  if((Second == "Snt") || (Second == "Snt-sh"))
11367  // third has to be 2 element idents with a space between
11368  {
11369  int SpacePos = Third.Pos(' ');
11370  if(SpacePos == 0)
11371  {
11372  Utilities->CallLogPop(919);
11373  return(false); // no space
11374  }
11375  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
11376  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
11377  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
11378  if(CheckLocationsExistInRailway)
11379  {
11380  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
11381  {
11382  Utilities->CallLogPop(920);
11383  return(false);
11384  }
11385  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
11386  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
11387  }
11388  if(Second == "Snt")
11389  {
11390  FormatType = StartNew;
11391  SequenceType = Start;
11392  LocationType = NoLocation;
11393  // can't be set until know whether located or not - done in SecondPassActions
11394  ShuttleLinkType = NotAShuttleLink;
11395  }
11396  else // Snt-sh
11397  {
11398  FormatType = SNTShuttle;
11399  LocationType = AtLocation;
11400  SequenceType = Start;
11401  ShuttleLinkType = ShuttleLink;
11402  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
11403  {
11404  Utilities->CallLogPop(1038);
11405  return(false);
11406  }
11407  }
11408  Utilities->CallLogPop(921);
11409  return(true);
11410  }
11411  if(Second == "Sns-sh") // third & fourth have to be headcodes
11412  {
11413  FormatType = SNSShuttle;
11414  LocationType = AtLocation;
11415  SequenceType = Start;
11416  ShuttleLinkType = ShuttleLink;
11417  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
11418  {
11419  Utilities->CallLogPop(1039);
11420  return(false);
11421  }
11422  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
11423  {
11424  Utilities->CallLogPop(1040);
11425  return(false);
11426  }
11427  Utilities->CallLogPop(1041);
11428  return(true);
11429  }
11430  if(Second == "F-nshs")
11431  {
11432  FormatType = FNSNonRepeatToShuttle;
11433  LocationType = AtLocation;
11434  SequenceType = Finish;
11435  ShuttleLinkType = ShuttleLink;
11436  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
11437  {
11438  Utilities->CallLogPop(1047);
11439  return(false);
11440  }
11441  Utilities->CallLogPop(1048);
11442  return(true);
11443  }
11444  if(Second == "Sns-fsh")
11445  {
11446  FormatType = SNSNonRepeatFromShuttle;
11447  LocationType = AtLocation;
11448  SequenceType = Start;
11449  ShuttleLinkType = ShuttleLink;
11450  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
11451  {
11452  Utilities->CallLogPop(1098);
11453  return(false);
11454  }
11455  Utilities->CallLogPop(1099);
11456  return(true);
11457  }
11458  if(Second == "Fns-sh") // third & fourth have to be headcodes
11459  {
11460  FormatType = FSHNewService;
11461  LocationType = AtLocation;
11462  SequenceType = Finish;
11463  ShuttleLinkType = ShuttleLink;
11464  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
11465  {
11466  Utilities->CallLogPop(1050);
11467  return(false);
11468  }
11469  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
11470  {
11471  Utilities->CallLogPop(1051);
11472  return(false);
11473  }
11474  Utilities->CallLogPop(1052);
11475  return(true);
11476  }
11477  // new segment for 'pas'
11478  if(Second == "pas") // third has to be a location
11479  {
11480  FormatType = PassTime;
11481  LocationType = EnRoute;
11482  SequenceType = Intermediate;
11483  ShuttleLinkType = NotAShuttleLink;
11484  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
11485  {
11486  Utilities->CallLogPop(1515);
11487  return(false);
11488  }
11489  Utilities->CallLogPop(1516);
11490  return(true);
11491  }
11492  // new segment for revised 'Fer'
11493  if(Second == "Fer")
11494  // third has to be a set of IDs separated by spaces, and at least 1
11495  {
11496  FormatType = ExitRailway;
11497  LocationType = EnRoute;
11498  SequenceType = Finish;
11499  ShuttleLinkType = NotAShuttleLink;
11500  if(CheckLocationsExistInRailway)
11501  {
11502  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
11503  {
11504  Utilities->CallLogPop(1519);
11505  return(false);
11506  }
11507  }
11508  Utilities->CallLogPop(1520);
11509  return(true);
11510  }
11511  // all remainder must be TimeCmdHeadCode types to be valid
11512  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
11513  (Second != "Frh-sh"))
11514  {
11515  Utilities->CallLogPop(922);
11516  return(false); // all TimeCmdHeadCode types
11517  }
11518  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
11519  {
11520  Utilities->CallLogPop(923);
11521  return(false);
11522  }
11523  FormatType = TimeCmdHeadCode;
11524  LocationType = AtLocation;
11525  if(Second == "Frh-sh")
11526  {
11527  ShuttleLinkType = ShuttleLink;
11528  }
11529  else
11530  {
11531  ShuttleLinkType = NotAShuttleLink;
11532  }
11533  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
11534  {
11535  SequenceType = Finish;
11536  }
11537  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
11538  {
11539  SequenceType = Intermediate;
11540  }
11541  if((Second == "Sfs") || (Second == "Sns"))
11542  {
11543  SequenceType = Start;
11544  }
11545  Utilities->CallLogPop(924);
11546  return(true);
11547 }
11548 
11549 // ---------------------------------------------------------------------------
11550 
11551 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
11552 {
11553  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
11554  // and contains no special characters
11555  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
11556  if(LocStr == "")
11557  {
11558  Utilities->CallLogPop(1353);
11559  return(false); // has to have at least one character
11560  }
11561  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
11562  {
11563  Utilities->CallLogPop(1354);
11564  return(false); // can't begin with a number
11565  }
11566  for(int x = 1; x < LocStr.Length() + 1; x++)
11567  {
11568  if(LocStr[x] < ' ')
11569  {
11570  Utilities->CallLogPop(1355);
11571  return(false); // contains a special character
11572  }
11573  if(LocStr[x] > 'z')
11574  {
11575  Utilities->CallLogPop(1356);
11576  return(false); // contains a character outside the standard ASCII set
11577  }
11578  }
11579  // check exists in railway location list if CheckLocationsExistInRailway is true
11580  if(CheckLocationsExistInRailway)
11581  {
11582  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
11583  {
11584  TimetableMessage(GiveMessages, "Location name '" + LocStr +
11585  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
11586  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
11587  "that includes a continuation will not be valid.");
11588  Utilities->CallLogPop(1357);
11589  return(false);
11590  }
11591  }
11592  Utilities->CallLogPop(1358);
11593  return(true);
11594 }
11595 
11596 // ---------------------------------------------------------------------------
11597 
11598 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
11599 {
11600  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
11601  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
11602  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
11603  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
11604  HeadCode);
11605  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
11606  {
11607  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
11608  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
11609  Utilities->CallLogPop(1359);
11610  return(false);
11611  }
11612  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
11613  for(int x = 1; x < (HeadCode.Length() + 1); x++)
11614  {
11615  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
11616  {
11617  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
11618  Utilities->CallLogPop(1895);
11619  return(false);
11620  }
11621  }
11622  // secondly ensure the true Headcode only has letters or digits
11623  for(int x = 3; x >= 0; x--)
11624  {
11625  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11626  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11627  {
11628  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11629  Utilities->CallLogPop(1790);
11630  return(false);
11631  }
11632  }
11633  Utilities->CallLogPop(1364);
11634  return(true);
11635 }
11636 
11637 // ---------------------------------------------------------------------------
11638 
11639 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
11640 // set of track element IDs, separated by spaces, and at least 1 present
11641 {
11642  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndPopulateListOfIDs," + IDSet); //had wrong title, changed at v2.13.0
11643  ExitList.clear();
11644  AnsiString CurrentID = "";
11645 
11646  if(IDSet.Length() == 0)
11647  {
11648  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11649  Utilities->CallLogPop(1521);
11650  return(false);
11651  }
11652  for(int x = 1; x <= IDSet.Length(); x++)
11653  {
11654  char C = IDSet[x];
11655  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11656  {
11657  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11658  Utilities->CallLogPop(1522);
11659  return(false);
11660  }
11661 /* don't use, error checks in GetTrackVectorPositionFromString instead
11662  if(C == '-') //this section added at v2.13.0 because of Amon Sadler's error file submitted 24/03/22
11663  {
11664  if((x==1) || (x == IDSet.Length()))
11665  {
11666  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
11667  Utilities->CallLogPop(2479);
11668  return(false);
11669  }
11670  if((IDSet[x-1] < '0') || (IDSet[x-1] > '9') || (IDSet[x+1] < '0') || (IDSet[x+1] > '9'))
11671  {
11672  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
11673  Utilities->CallLogPop(2480);
11674  return(false);
11675  }
11676  }
11677 */
11678  }
11679  int Pos = IDSet.Pos(' '); // look for the first space
11680 
11681  while(true)
11682  {
11683  if(Pos == 0)
11684  {
11685  CurrentID = IDSet;
11686  IDSet = "";
11687  }
11688  else
11689  {
11690  CurrentID = IDSet.SubString(1, Pos - 1);
11691  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11692  }
11693  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11694  if(VecPos == -1)
11695  {
11696  Utilities->CallLogPop(1523);
11697  return(false); // messages given in GetTrackVectorPositionFromString
11698  }
11699  else
11700  {
11701  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11702  {
11703  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
11704  Utilities->CallLogPop(1524);
11705  return(false);
11706  }
11707  else
11708  {
11709  // first check for duplicates
11710  if(!ExitList.empty())
11711  {
11712  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
11713  {
11714  if(*ELIT == VecPos)
11715  {
11716  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
11717  Utilities->CallLogPop(1532);
11718  return(false);
11719  }
11720  }
11721  }
11722  // of OK add it to the list
11723  ExitList.push_back(VecPos);
11724  }
11725  }
11726  if(IDSet == "")
11727  {
11728  Utilities->CallLogPop(1525);
11729  return(true);
11730  }
11731  else
11732  {
11733  Pos = IDSet.Pos(' '); // look for the next space
11734  }
11735  } // while(true)
11736 }
11737 
11738 // ---------------------------------------------------------------------------
11739 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
11740  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
11741 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
11742 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
11743 // of each item
11744 {
11745  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
11746  int Pos = 0;
11747  AnsiString Remainder = "";
11748  int SemiColonCount = 0;
11749 
11750  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
11751  {
11752  if(TrainInfoStr[x] == ';')
11753  {
11754  SemiColonCount++;
11755  }
11756  }
11757  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
11758  {
11759  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
11760  "'. Should be headcode + optional description for a continuing service;" +
11761  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
11762  Utilities->CallLogPop(880);
11763  return(false);
11764  }
11765  if(SemiColonCount == 0)
11766  {
11767  HeadCode = TrainInfoStr;
11768  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
11769  {
11770  Utilities->CallLogPop(881);
11771  return(false);
11772  }
11773  Utilities->CallLogPop(882);
11774  return(true);
11775  }
11776  if(SemiColonCount == 1) // headcode & description only
11777  {
11778  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11779  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11780  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11781  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
11782  {
11783  Utilities->CallLogPop(883);
11784  return(false);
11785  }
11786  if(Description == "")
11787  {
11788  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11789  Utilities->CallLogPop(884);
11790  return(false);
11791  }
11792  if(Description.Length() > 60)
11793  {
11794  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11795  Utilities->CallLogPop(1157);
11796  return(false);
11797  }
11798  for(int x = 1; x < Description.Length() + 1; x++)
11799  {
11800  if((Description[x] < ' ') || (Description[x] > '~'))
11801  {
11802  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11803  Utilities->CallLogPop(885);
11804  return(false);
11805  }
11806  }
11807  Utilities->CallLogPop(886);
11808  return(true);
11809  }
11810  // if here must have 6 or 7 semicolons
11811  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11812  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11813  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11814  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
11815  {
11816  Utilities->CallLogPop(887);
11817  return(false);
11818  }
11819  Pos = Remainder.Pos(';'); // 2nd delimiter
11820  Description = Remainder.SubString(1, Pos - 1);
11821  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11822  if(Description == "")
11823  {
11824  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11825  Utilities->CallLogPop(888);
11826  return(false);
11827  }
11828  if(Description.Length() > 60)
11829  {
11830  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11831  Utilities->CallLogPop(1158);
11832  return(false);
11833  }
11834  for(int x = 1; x < Description.Length() + 1; x++)
11835  {
11836  if((Description[x] < ' ') || (Description[x] > 126))
11837  {
11838  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11839  Utilities->CallLogPop(889);
11840  return(false);
11841  }
11842  }
11843  Pos = Remainder.Pos(';'); // 3rd delimiter
11844  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
11845 
11846  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11847  if(StartSpeedStr == "")
11848  {
11849  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
11850  Utilities->CallLogPop(890);
11851  return(false);
11852  }
11853  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
11854  {
11855  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
11856  {
11857  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
11858  Utilities->CallLogPop(891);
11859  return(false);
11860  }
11861  }
11862  StartSpeed = StartSpeedStr.ToInt();
11863  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11864  {
11865  StartSpeed = TTrain::MaximumSpeedLimit;
11866  if(!SSHigh) // added at v2.4.0
11867  {
11868  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11869  SSHigh = true;
11870  }
11871  }
11872  Pos = Remainder.Pos(';'); // 4th delimiter
11873  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
11874 
11875  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11876  if(MaxRunningSpeedStr == "")
11877  {
11878  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
11879  Utilities->CallLogPop(892);
11880  return(false);
11881  }
11882  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
11883  {
11884  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
11885  {
11886  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
11887  Utilities->CallLogPop(893);
11888  return(false);
11889  }
11890  }
11891  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
11892  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11893  {
11894  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
11895  if(!MRSHigh) // added at v2.4.0
11896  {
11897  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11898  MRSHigh = true;
11899  }
11900  }
11901  if(MaxRunningSpeed < 10)
11902  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11903  {
11904  MaxRunningSpeed = 10;
11905  if(!MRSLow) // added at v2.4.0
11906  {
11907  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11908  MRSLow = true;
11909  }
11910  }
11911  Pos = Remainder.Pos(';'); // 5th delimiter
11912  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
11913 
11914  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11915  if(MassStr == "")
11916  {
11917  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
11918  Utilities->CallLogPop(895);
11919  return(false);
11920  }
11921  for(int x = 1; x < MassStr.Length() + 1; x++)
11922  {
11923  if((MassStr[x] < '0') || (MassStr[x] > '9'))
11924  {
11925  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
11926  Utilities->CallLogPop(896);
11927  return(false);
11928  }
11929  }
11930  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
11931  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
11932  {
11933  Mass = TTrain::MaximumMassLimit;
11934  if(!MassHigh) // added at v2.4.0
11935  {
11936  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
11937  MassHigh = true;
11938  }
11939  }
11940  if(Mass == 0)
11941  {
11942  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
11943  Utilities->CallLogPop(897);
11944  return(false);
11945  }
11946  Pos = Remainder.Pos(';'); // 6th delimiter
11947  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
11948 
11949  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11950  if(MaxBrakeForceStr == "")
11951  {
11952  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
11953  Utilities->CallLogPop(898);
11954  return(false);
11955  }
11956  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
11957  {
11958  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
11959  {
11960  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
11961  Utilities->CallLogPop(899);
11962  return(false);
11963  }
11964  }
11965  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
11966 
11967  // convert to kg force
11968  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
11969  {
11970  MaxBrakeForce = Mass;
11971  if(!BFHigh) // added at v2.4.0
11972  {
11973  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
11974  BFHigh = true;
11975  }
11976  }
11977  if((MaxBrakeForce / Mass) < 0.01)
11978  {
11979  MaxBrakeForce = Mass * 0.01;
11980  if(!BFLow) // added at v2.4.0
11981  {
11982  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
11983  BFLow = true;
11984  }
11985  }
11986  // convert to m/s/s
11987  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
11988  // now may have just a power entry or power and signaller max. speed
11989  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
11990 
11991  if(SemiColonCount == 6)
11992  {
11993  GrossPowerStr = Remainder;
11994  SignallerSpeedStr = "30"; // default value
11995  }
11996  else // must be 7
11997  {
11998  Pos = Remainder.Pos(';'); // 7th delimiter
11999  GrossPowerStr = Remainder.SubString(1, Pos - 1);
12000  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12001  }
12002  // deal with GrossPower
12003  if(GrossPowerStr == "")
12004  {
12005  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
12006  Utilities->CallLogPop(901);
12007  return(false);
12008  }
12009  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
12010  {
12011  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
12012  {
12013  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
12014  Utilities->CallLogPop(902);
12015  return(false);
12016  }
12017  }
12018 
12019  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
12020 
12021  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
12022  {
12023  GrossPower = TTrain::MaximumPowerLimit;
12024  if(!PwrHigh)
12025  {
12026  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
12027  PwrHigh = true;
12028  }
12029  }
12030  else if(GrossPower == 0) // changed at v2.4.0
12031  {
12032  GrossPower = 0.1;
12033  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
12034  }
12035  else if((GrossPower > 0) && (GrossPower < 10000))
12036  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
12037  {
12038  GrossPower = 10000;
12039  }
12040  PowerAtRail = GrossPower * 0.8;
12041  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
12042 
12043  // deal with SignallerSpeed
12044  if(SignallerSpeedStr == "")
12045  {
12046  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
12047  Utilities->CallLogPop(1771);
12048  return(false);
12049  }
12050  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
12051  {
12052  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
12053  {
12054  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
12055  Utilities->CallLogPop(1769);
12056  return(false);
12057  }
12058  }
12059  SignallerSpeed = SignallerSpeedStr.ToInt();
12060  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
12061  {
12062  SignallerSpeed = TTrain::MaximumSpeedLimit;
12063  if(!SigSHigh)
12064  {
12065  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12066  SigSHigh = true;
12067  }
12068  }
12069  if(SignallerSpeed < 10)
12070  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12071  {
12072  SignallerSpeed = 10;
12073  if(!SigSLow)
12074  {
12075  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12076  SigSLow = true;
12077  }
12078  // Utilities->CallLogPop(1770);
12079  // return false;
12080  }
12081  Utilities->CallLogPop(904);
12082  return(true);
12083 }
12084 
12085 // ---------------------------------------------------------------------------
12086 
12087 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
12088  bool GiveMessages)
12089 {
12090  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
12091  // function checks validity of each item and returns false for error
12092  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
12093  if(OneEntry.Length() < 7)
12094  {
12095  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12096  Utilities->CallLogPop(865);
12097  return(false);
12098  }
12099  int SemiColonCount = 0;
12100 
12101  for(int x = 1; x < OneEntry.Length() + 1; x++)
12102  {
12103  if(OneEntry[x] == ';')
12104  {
12105  SemiColonCount++;
12106  }
12107  }
12108  if(SemiColonCount != 3)
12109  {
12110  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12111  Utilities->CallLogPop(866);
12112  return(false);
12113  }
12114  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
12115  {
12116  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12117  Utilities->CallLogPop(867);
12118  return(false);
12119  }
12120  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
12121  // strip off R;
12122 
12123  int Pos = 0;
12124 
12125  Pos = Remainder.Pos(';');
12126  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
12127 
12128  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12129  if(MinutesStr == "")
12130  {
12131  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
12132  Utilities->CallLogPop(868);
12133  return(false);
12134  }
12135  if(MinutesStr.Length() > 3)
12136  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
12137  {
12138  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
12139  Utilities->CallLogPop(2119);
12140  return(false);
12141  }
12142  for(int x = 1; x < MinutesStr.Length() + 1; x++)
12143  {
12144  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
12145  {
12146  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
12147  Utilities->CallLogPop(869);
12148  return(false);
12149  }
12150  }
12151  RearStartOrRepeatMins = MinutesStr.ToInt();
12152  if(RearStartOrRepeatMins == 0)
12153  {
12154  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
12155  Utilities->CallLogPop(870);
12156  return(false);
12157  }
12158  Pos = Remainder.Pos(';');
12159  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
12160 
12161  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12162  if(DigitsStr == "")
12163  {
12164  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
12165  Utilities->CallLogPop(871);
12166  return(false);
12167  }
12168  for(int x = 1; x < DigitsStr.Length() + 1; x++)
12169  {
12170  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
12171  {
12172  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
12173  Utilities->CallLogPop(872);
12174  return(false);
12175  }
12176  }
12177  if(DigitsStr.Length() > 2)
12178  {
12179  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
12180  Utilities->CallLogPop(873);
12181  return(false);
12182  }
12183  FrontStartOrRepeatDigits = DigitsStr.ToInt();
12184 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
12185  route rather than the service
12186  if(FrontStartOrRepeatDigits == 0)
12187  {
12188  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
12189  Utilities->CallLogPop(874);
12190  return false;
12191  }
12192 */
12193  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
12194  // new for v0.6b for unrestricted headcodes
12195  {
12196  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
12197  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
12198  Utilities->CallLogPop(1889);
12199  return(false);
12200  }
12201  AnsiString NumberStr = Remainder;
12202 
12203  if(NumberStr == "")
12204  {
12205  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
12206  Utilities->CallLogPop(875);
12207  return(false);
12208  }
12209  if(NumberStr.Length() > 4)
12210  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
12211  {
12212  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
12213  Utilities->CallLogPop(2118);
12214  return(false);
12215  }
12216  for(int x = 1; x < NumberStr.Length() + 1; x++)
12217  {
12218  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
12219  // catches negative numbers
12220  {
12221  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
12222  Utilities->CallLogPop(876);
12223  return(false);
12224  }
12225  }
12226  NumberOfRepeats = NumberStr.ToInt();
12227  if(NumberOfRepeats == 0)
12228  {
12229  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
12230  Utilities->CallLogPop(877);
12231  return(false);
12232  }
12233  Utilities->CallLogPop(878);
12234  return(true);
12235 }
12236 
12237 // ---------------------------------------------------------------------------
12238 
12239 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
12240 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
12241  vector rather than the timetable
12242  Note also that for unlocated Snt entries the LocationType hasn't yet been set
12243 
12244  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
12245 
12246  For info:-
12247  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
12248  {
12249  public:
12250  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
12252  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
12253  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
12254  int NumberOfRepeats; ///< the number of repeating services
12255  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
12257  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
12259  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
12260  TTimetableFormatType FormatType; ///< defines the timetable action type
12261  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
12262  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
12263  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
12264  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
12266  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
12268 
12269  // inline function
12270 
12272  TActionVectorEntry() {
12273  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
12274  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
12275  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
12276  Warning = false; SignallerControl = false;
12277  }
12278  };
12279 
12280  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
12281 
12282  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
12283  {
12284  public:
12285  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
12288  double MaxBrakeRate; ///< in metres/sec/sec
12289  double MaxRunningSpeed; ///< in km/h
12290  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
12291  int Mass; ///< in kg
12292  int NumberOfTrains; ///< number of repeats + 1
12293  int SignallerSpeed; ///< in km/h for use when under signaller control
12294  int StartSpeed; ///< in km/h
12295  TActionVector ActionVector; ///< all the actions for the train
12296  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
12297 
12298  //inline function
12299 
12301  TTrainDataEntry()
12302  {
12303  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
12304  }
12305  };
12306 
12307  Allowable successors:-
12308  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
12309  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
12310  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
12311  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12312  set location, else fails)
12313  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12314  set location, else fails)
12315  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12316  set location, else fails)
12317  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12318  set location, else fails)
12319  Fns -> R only
12320  F-nshs -> Nothing (no repeats permitted)
12321  Fjo -> R only
12322  Frh -> R only
12323  Fer -> R only
12324  Frh-sh -> R only
12325  Fns-sh -> R only
12326  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
12327  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12328  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12329  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12330  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12331  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12332  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12333  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12334  Repeat -> Nothing
12335 
12336  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
12337  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
12338  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
12339  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
12340  Check all times increase or stay same through ActionVector
12341  Cycle through all entries in vector setting arr & dep times based on above list
12342  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
12343  Check locations match the arr & dep TimeLoc entries
12344  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
12345  Make above valid succession checks
12346  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
12347  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
12348  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12349  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
12350  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
12351  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12352  Set train info for Sfs & Sns entries
12353  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
12354  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
12355  element at each end, or length of 3 & 1 extra element at either end
12356  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
12357  Check all Cmds have EventTime set & Arr & Dep times = -1
12358  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
12359  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
12360 
12361  Give messages in function if errors detected and clear the vector. Return false for failure.
12362 */
12363 
12364 /* Earlier checks:-
12365  Checks carried out with error messages in this function:-
12366  At least one comma in the line (it's based on a csv file);
12367  No entries following train information;
12368  At least one comma in remainder after train information (i.e at least a start and a finish entry);
12369  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
12370  First entry not a start entry;
12371  Train information incomplete before a start entry;
12372  Entry follows a finish entry but doesn't begin with 'R';
12373  SplitEntry returns false in a finish entry - message repeats the entry for information;
12374  Last action entry isn't a finish entry.
12375 
12376  Function returns false with no message if:-
12377  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
12378  time is found at all then an error message is given in the calling function);
12379  SplitTrainInfo returns false (message given in called function);
12380  SplitRepeat returns false (message given in called function).
12381 
12382 Double crosslink (shuttle) table: [OtherHeadCode, NonRepeatingShuttleLinkHeadCode, LinkedTrainEntryPtr, NonRepeatingShuttleLinkEntryPtr] <-- these for easier searching for this table
12383 
12384 Command Format OtherHead NonRepeating- LinkedTrain- NonRepeating- Decsription
12385  Code ShuttleLink- EntryPtr ShuttleLink
12386  HeadCode EntryPtr
12387 
12388 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
12389 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
12390 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
12391 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
12392 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
12393 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
12394 
12395 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
12396 
12397 */{
12398  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
12399  if(TrainDataVector.empty())
12400  {
12401  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
12402  TrainDataVector.clear();
12403  Utilities->CallLogPop(1832);
12404  return(false);
12405  }
12406 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
12407  1) must have at least one actionvector entry
12408  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
12409  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
12410  4) first entry must be a start;
12411  4a) if first entry is Snt and not signallercontrol and second is a finish then it must be at a location with zero start speed
12412  5) a start must be the first entry;
12413  6) a repeat entry must be the last;
12414  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
12415  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
12416  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
12417 */
12418 
12419  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
12420  TwoLocationFlag = false; //added at v2.9.1
12421  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
12422  {
12423  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12424  if(TrainDataVector.at(x).ActionVector.empty())
12425  {
12426  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
12427  TrainDataVector.clear();
12428  Utilities->CallLogPop(1833);
12429  return(false);
12430  }
12431  }
12432  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
12433  {
12434  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12435  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12436  if(!(AVEntry0.SignallerControl))
12437  {
12438  if(TrainDataVector.at(x).ActionVector.size() == 1)
12439  {
12440  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
12441  TrainDataVector.clear();
12442  Utilities->CallLogPop(1822);
12443  return(false);
12444  }
12445  }
12446  }
12447  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
12448  {
12449  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12450  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12451  if(AVEntry0.SignallerControl)
12452  {
12453  if(TrainDataVector.at(x).ActionVector.size() > 2)
12454  {
12455  SecondPassMessage(GiveMessages,
12456  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
12457  TDEntry.HeadCode);
12458  TrainDataVector.clear();
12459  Utilities->CallLogPop(1837);
12460  return(false);
12461  }
12462  if(TrainDataVector.at(x).ActionVector.size() > 1)
12463  {
12464  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12465  if(AVEntry1.FormatType != Repeat)
12466  {
12467  SecondPassMessage(GiveMessages,
12468  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
12469  TrainDataVector.clear();
12470  Utilities->CallLogPop(1838);
12471  return(false);
12472  }
12473  }
12474  }
12475  }
12476  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
12477  {
12478  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12479  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12480  if(AVEntry0.SequenceType != Start)
12481  {
12482  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
12483  TrainDataVector.clear();
12484  Utilities->CallLogPop(1824);
12485  return(false);
12486  }
12487  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
12488  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
12489  // and others for a located Snt, but those checks done later
12490  {
12491  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12492  // must be a second entry if first not signallercontrol
12493  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
12494  {
12495  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
12496  TDEntry.HeadCode);
12497  TrainDataVector.clear();
12498  Utilities->CallLogPop(2046);
12499  return(false);
12500  }
12501  }
12502  }
12503  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
12504  {
12505  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12506  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12507  {
12508  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12509  if((AVEntry.SequenceType == Start) && (y != 0))
12510  {
12511  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
12512  TrainDataVector.clear();
12513  Utilities->CallLogPop(1825);
12514  return(false);
12515  }
12516  }
12517  }
12518  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
12519  {
12520  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12521  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12522  {
12523  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12524  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
12525  {
12526  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
12527  TrainDataVector.clear();
12528  Utilities->CallLogPop(1826);
12529  return(false);
12530  }
12531  }
12532  }
12533  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
12534  {
12535  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12536  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12537  {
12538  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12539  if((y == 0) && AVEntry.SignallerControl)
12540  {
12541  break;
12542  }
12543  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
12544  {
12545  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
12546  {
12547  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
12548  TrainDataVector.clear();
12549  Utilities->CallLogPop(1827);
12550  return(false);
12551  }
12552  if(AVEntry.FormatType == Repeat)
12553  {
12554  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
12555  if(LastAVEntry.SequenceType != Finish)
12556  {
12557  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
12558  TrainDataVector.clear();
12559  Utilities->CallLogPop(1828);
12560  return(false);
12561  }
12562  }
12563  }
12564  }
12565  }
12566  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
12567  {
12568  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12569  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12570  {
12571  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12572  if(AVEntry.SequenceType == Finish)
12573  {
12574  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
12575  {
12576  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
12577  TrainDataVector.clear();
12578  Utilities->CallLogPop(1829);
12579  return(false);
12580  }
12581  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
12582  {
12583  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12584  {
12585  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
12586  TDEntry.HeadCode);
12587  TrainDataVector.clear();
12588  Utilities->CallLogPop(1830);
12589  return(false);
12590  }
12591  }
12592  }
12593  }
12594  }
12595 
12596  // end of new preliminary checks
12597 
12598  // check ActionVector present and check start event successor validity
12599  // For Snt & Snt-sh set location if stopped, don't set any times yet
12600  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12601  {
12602  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12603  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12604  // use reference so can change internals where necessary
12605  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
12606  {
12607  AnsiString LocationName = "";
12608  if(IsSNTEntryLocated(0, TDEntry, LocationName))
12609  // it is at a location
12610  {
12611  if(TDEntry.StartSpeed == 0) // stopped
12612  {
12613  AVEntry0.LocationName = LocationName;
12614  AVEntry0.LocationType = AtLocation;
12615  // check successor validity for located Snt that isn't a SignallerControl entry
12616  if(!AVEntry0.SignallerControl)
12617  {
12618  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12619  // at least 2 entries present checked in integrity check so (1) valid
12620  if(!AtLocSuccessor(AVEntry1))
12621  {
12622  // Frh following Snt-sh will return false in location check, so no need to check here
12623  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
12624  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12625  TrainDataVector.clear();
12626  Utilities->CallLogPop(523);
12627  return(false);
12628  }
12629  }
12630  }
12631  else
12632  {
12633  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
12634  TDEntry.HeadCode);
12635  TrainDataVector.clear();
12636  Utilities->CallLogPop(791);
12637  return(false);
12638  }
12639  }
12640  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
12641  {
12642  if(AVEntry0.Command == "Snt-sh")
12643  {
12644  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
12645  TrainDataVector.clear();
12646  Utilities->CallLogPop(1042);
12647  return(false);
12648  }
12649  AVEntry0.LocationType = EnRoute;
12650  if(!AVEntry0.SignallerControl)
12651  {
12652  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12653  // at least 2 entries checked in integrity check so (1) valid
12654  if(!MovingSuccessor(AVEntry1))
12655  {
12656  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
12657  TDEntry.HeadCode);
12658  TrainDataVector.clear();
12659  Utilities->CallLogPop(790);
12660  return(false);
12661  }
12662  }
12663  }
12664  }
12665  // check other start successors
12666  else if(AVEntry0.SequenceType == Start)
12667  {
12668  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12669  // at least 2 entries present checked in integrity check so (1) valid
12670  if(!AtLocSuccessor(AVEntry1))
12671  {
12672  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12673  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12674  TrainDataVector.clear();
12675  Utilities->CallLogPop(793);
12676  return(false);
12677  }
12678  }
12679  }
12680 
12681  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12682  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12683  {
12684  bool FoundFlag = false;
12685  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12686  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12687  // use reference so can change internals
12688  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12689  {
12690  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12691  {
12692  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12693  if(AVEntry2.FormatType == TimeLoc)
12694  {
12695  FoundFlag = true;
12696  AVEntry.LocationName = AVEntry2.LocationName;
12697  break;
12698  }
12699  }
12700  if(!FoundFlag)
12701  {
12702  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
12703  TDEntry.HeadCode);
12704  TrainDataVector.clear();
12705  Utilities->CallLogPop(851);
12706  return(false);
12707  }
12708  }
12709  }
12710 
12711  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
12712  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12713  {
12714  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12715  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12716  {
12717  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12718  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
12719  {
12720  if(AVEntry.LocationName == "")
12721  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
12722  {
12723  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
12724  ": an event should have had a location name associated with it but it could not be found");
12725  TrainDataVector.clear();
12726  Utilities->CallLogPop(1831);
12727  return(false);
12728  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
12729  }
12730  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
12731  {
12732  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
12733  // use reference so can change internals where necessary
12734  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
12735  {
12736  AVEntry2.LocationName = AVEntry.LocationName;
12737  }
12738  else
12739  {
12740  break;
12741  }
12742  }
12743  }
12744  }
12745  }
12746  // all location names now set
12747 
12748  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12749  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12750  {
12751  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12752  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12753  {
12754  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12755  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
12756  {
12757  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
12758  // i.e at least one more, must be a repeat
12759  {
12760  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12761  {
12762  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
12763  TrainDataVector.clear();
12764  Utilities->CallLogPop(798);
12765  return(false);
12766  }
12767  }
12768  }
12769  if(AVEntry.Command == "F-nshs")
12770  {
12771  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12772  // i.e has to be the last
12773  {
12774  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
12775  TrainDataVector.clear();
12776  Utilities->CallLogPop(1049);
12777  return(false);
12778  }
12779  }
12780  if(AVEntry.Command == "pas")
12781  {
12782  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12783  {
12784  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
12785  TrainDataVector.clear();
12786  Utilities->CallLogPop(1518);
12787  return(false);
12788  }
12789  }
12790  if(AVEntry.Command == "jbo")
12791  {
12792  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12793  {
12794  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
12795  TrainDataVector.clear();
12796  Utilities->CallLogPop(800);
12797  return(false);
12798  }
12799  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12800  if(!AtLocSuccessor(AVEntry2))
12801  {
12802  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
12803  ". The event isn't valid for a stationary train.");
12804  TrainDataVector.clear();
12805  Utilities->CallLogPop(801);
12806  return(false);
12807  }
12808  }
12809  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
12810  {
12811  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12812  {
12813  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
12814  TrainDataVector.clear();
12815  Utilities->CallLogPop(802);
12816  return(false);
12817  }
12818  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12819  if(!AtLocSuccessor(AVEntry2))
12820  {
12821  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
12822  ". The event isn't valid for a stationary train.");
12823  TrainDataVector.clear();
12824  Utilities->CallLogPop(803);
12825  return(false);
12826  }
12827  }
12828  if(AVEntry.Command == "cdt")
12829  {
12830  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12831  {
12832  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
12833  TrainDataVector.clear();
12834  Utilities->CallLogPop(804);
12835  return(false);
12836  }
12837  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12838  if(!AtLocSuccessor(AVEntry2))
12839  {
12840  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
12841  ". The event isn't valid for a stationary train.");
12842  TrainDataVector.clear();
12843  Utilities->CallLogPop(805);
12844  return(false);
12845  }
12846  }
12847  if(AVEntry.FormatType == TimeTimeLoc)
12848  {
12849  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12850  {
12851  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
12852  TrainDataVector.clear();
12853  Utilities->CallLogPop(806);
12854  return(false);
12855  }
12856  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12857  if(!MovingSuccessor(AVEntry2))
12858  {
12859  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
12860  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
12861  TrainDataVector.clear();
12862  Utilities->CallLogPop(807);
12863  return(false);
12864  }
12865  }
12866  if(AVEntry.FormatType == PassTime)
12867  {
12868  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12869  {
12870  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
12871  TrainDataVector.clear();
12872  Utilities->CallLogPop(1530);
12873  return(false);
12874  }
12875  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12876  if(!MovingSuccessor(AVEntry2))
12877  {
12878  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
12879  ". The event isn't valid for a moving train.");
12880  TrainDataVector.clear();
12881  Utilities->CallLogPop(1531);
12882  return(false);
12883  }
12884  }
12885  if(AVEntry.FormatType == Repeat)
12886  {
12887  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12888  {
12889  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
12890  TrainDataVector.clear();
12891  Utilities->CallLogPop(808);
12892  return(false);
12893  }
12894  }
12895  }
12896  }
12897 
12898  // set arrival & departure times for TimeLocs & set their EventTimes to -1
12899  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12900  {
12901  bool LastEntryIsAnArrival = false;
12902  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12903  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
12904  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12905  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12906  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
12907  {
12908  LastEntryIsAnArrival = false;
12909  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12910  {
12911  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12912  if(AVEntry.FormatType == TimeLoc)
12913  {
12914  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12915  {
12916  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12917  }
12918  if(LastEntryIsAnArrival)
12919  {
12920  AVEntry.DepartureTime = AVEntry.EventTime;
12921  AVEntry.EventTime = TDateTime(-1);
12922  LastEntryIsAnArrival = false;
12923  }
12924  else // last entry a departure
12925  {
12926  AVEntry.ArrivalTime = AVEntry.EventTime;
12927  AVEntry.EventTime = TDateTime(-1);
12928  LastEntryIsAnArrival = true;
12929  }
12930  }
12931  }
12932  }
12933  else // all others stopped at beginning
12934  {
12935  LastEntryIsAnArrival = true;
12936  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12937  {
12938  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12939  if(AVEntry.FormatType == TimeLoc)
12940  {
12941  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12942  {
12943  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12944  }
12945  if(LastEntryIsAnArrival)
12946  {
12947  AVEntry.DepartureTime = AVEntry.EventTime;
12948  AVEntry.EventTime = TDateTime(-1);
12949  LastEntryIsAnArrival = false;
12950  }
12951  else // last entry a departure
12952  {
12953  AVEntry.ArrivalTime = AVEntry.EventTime;
12954  AVEntry.EventTime = TDateTime(-1);
12955  LastEntryIsAnArrival = true;
12956  }
12957  }
12958  }
12959  }
12960  }
12961  // perform remaining successor checks for TimeLocs
12962  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12963  {
12964  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12965  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12966  {
12967  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12968  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
12969  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
12970  {
12971  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12972  {
12973  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
12974  TrainDataVector.clear();
12975  Utilities->CallLogPop(809);
12976  return(false);
12977  }
12978  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12979  if(!AtLocSuccessor(AVEntry2))
12980  {
12981  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
12982  ". The event isn't valid for a stationary train.");
12983  TrainDataVector.clear();
12984  Utilities->CallLogPop(810);
12985  return(false);
12986  }
12987  }
12988  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
12989  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
12990  {
12991  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12992  {
12993  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
12994  TrainDataVector.clear();
12995  Utilities->CallLogPop(811);
12996  return(false);
12997  }
12998  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12999  if(!MovingSuccessor(AVEntry2))
13000  {
13001  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
13002  ". The event isn't valid for a moving train.");
13003  TrainDataVector.clear();
13004  Utilities->CallLogPop(812);
13005  return(false);
13006  }
13007  }
13008  }
13009  }
13010 
13011  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
13012  // & repeats have no times set
13013  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13014  {
13015  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13016  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13017  {
13018  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13019  if(AVEntry.FormatType == TimeLoc)
13020  {
13021  if(AVEntry.EventTime != TDateTime(-1))
13022  {
13023  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
13024  }
13025  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
13026  {
13027  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
13028  }
13029  }
13030  if(AVEntry.FormatType == TimeTimeLoc)
13031  {
13032  if(AVEntry.EventTime != TDateTime(-1))
13033  {
13034  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
13035  }
13036  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
13037  {
13038  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
13039  }
13040  }
13041  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
13042  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
13043  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
13044  {
13045  if(AVEntry.EventTime == TDateTime(-1))
13046  {
13047  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
13048  }
13049  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
13050  {
13051  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
13052  }
13053  }
13054  if(AVEntry.FormatType == Repeat)
13055  {
13056  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
13057  {
13058  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
13059  }
13060  }
13061  }
13062  }
13063 
13064  // check times stay same or increase, note that can have time of 0 if include midnight
13065  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13066  {
13067  TDateTime CurrentTime = TTClockTime; // the timetable start time
13068  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13069  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13070  {
13071  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13072  if(AVEntry.FormatType == Repeat)
13073  {
13074  break;
13075  }
13076  if(AVEntry.FormatType == FinRemHere)
13077  {
13078  break;
13079  }
13080  if(AVEntry.FormatType == TimeTimeLoc)
13081  {
13082  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
13083  {
13084  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
13085  TDEntry.HeadCode);
13086  TrainDataVector.clear();
13087  Utilities->CallLogPop(813);
13088  return(false);
13089  }
13090  if(AVEntry.ArrivalTime < CurrentTime)
13091  {
13092  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
13093  TDEntry.HeadCode);
13094  TrainDataVector.clear();
13095  Utilities->CallLogPop(814);
13096  return(false);
13097  }
13098  CurrentTime = AVEntry.DepartureTime;
13099  continue;
13100  }
13101  if(AVEntry.FormatType == TimeLoc)
13102  {
13103  if(AVEntry.ArrivalTime >= TDateTime(0))
13104  {
13105  if(AVEntry.ArrivalTime < CurrentTime)
13106  {
13107  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
13108  TrainDataVector.clear();
13109  Utilities->CallLogPop(815);
13110  return(false);
13111  }
13112  CurrentTime = AVEntry.ArrivalTime;
13113  }
13114  else
13115  {
13116  if(AVEntry.DepartureTime < CurrentTime)
13117  // both may be 0 legitimately so must allow for this
13118  {
13119  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
13120  TrainDataVector.clear();
13121  Utilities->CallLogPop(816);
13122  return(false);
13123  }
13124  CurrentTime = AVEntry.DepartureTime;
13125  }
13126  continue;
13127  }
13128  if(AVEntry.EventTime < CurrentTime)
13129  // all others have EventTime set
13130  {
13131  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
13132  ", may be before timetable start time");
13133  TrainDataVector.clear();
13134  Utilities->CallLogPop(835);
13135  return(false);
13136  }
13137  CurrentTime = AVEntry.EventTime;
13138  continue;
13139  }
13140  }
13141 
13142  // check locations consistent
13143  AnsiString LastLocationName = "";
13144 
13145  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13146  {
13147  bool LastEntryIsAnArrival = false;
13148  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13149  // first deal with moving Snt entries (all else stopped)
13150  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
13151  {
13152  LastEntryIsAnArrival = false;
13153  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
13154  if(LastLocationName != "")
13155  {
13156  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
13157  }
13158  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
13159  y++) // note that immediate successor to a moving Snt can only be a Moving type
13160  {
13161  // if it's a SignallerControl entry then the condition isn't met
13162  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13163  if(AVEntry.FormatType == Repeat)
13164  {
13165  break; // repeat = reached end (+allows repeat after signaller controlled entry)
13166  }
13167  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
13168  {
13169  if(AVEntry.LocationName != LastLocationName)
13170  {
13171  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13172  AVEntry.Command);
13173  TrainDataVector.clear();
13174  Utilities->CallLogPop(823);
13175  return(false);
13176  }
13177  }
13178  else if(AVEntry.FormatType == TimeCmd)
13179  // cdt is the only TimeCmd
13180  {
13181  if(AVEntry.LocationName != LastLocationName)
13182  {
13183  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13184  AVEntry.Command);
13185  TrainDataVector.clear();
13186  Utilities->CallLogPop(824);
13187  return(false);
13188  }
13189  }
13190  else if(AVEntry.FormatType == TimeTimeLoc)
13191  {
13192  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13193  // last entry must be a departure or would have failed earlier
13194  {
13195  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13196  TwoLocationFlag = true;
13197 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13198 // TwoOrMoreLocationsWarningGiven = true;
13199  }
13200  LastLocationName = AVEntry.LocationName;
13201  LastEntryIsAnArrival = false;
13202  }
13203  else if(AVEntry.FormatType == TimeLoc)
13204  {
13205  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
13206  {
13207  SecondPassMessage(GiveMessages,
13208  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
13209  TrainDataVector.clear();
13210  Utilities->CallLogPop(826);
13211  return(false);
13212  }
13213  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
13214  {
13215  SecondPassMessage(GiveMessages,
13216  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
13217  TrainDataVector.clear();
13218  Utilities->CallLogPop(827);
13219  return(false);
13220  }
13221  LastLocationName = AVEntry.LocationName;
13222  LastEntryIsAnArrival = !LastEntryIsAnArrival;
13223  }
13224  }
13225  }
13226  else // all stationary starting entries
13227  {
13228  LastEntryIsAnArrival = true;
13229  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
13230  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13231  {
13232  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13233  if(AVEntry.FormatType == Repeat)
13234  {
13235  break;
13236  }
13237  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
13238  // no need to add anything for shuttle starts since they are at loc (0) anyway
13239  {
13240  if(AVEntry.LocationName != LastLocationName)
13241  {
13242  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13243  AVEntry.Command);
13244  TrainDataVector.clear();
13245  Utilities->CallLogPop(828);
13246  return(false);
13247  }
13248  }
13249  else if(AVEntry.FormatType == TimeCmd)
13250  // cdt is the only TimeCmd
13251  {
13252  if(AVEntry.LocationName != LastLocationName)
13253  {
13254  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13255  AVEntry.Command);
13256  TrainDataVector.clear();
13257  Utilities->CallLogPop(829);
13258  return(false);
13259  }
13260  }
13261  else if(AVEntry.FormatType == TimeTimeLoc)
13262  {
13263  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13264  // last entry must be a departure or would have failed earlier
13265  {
13266  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13267  TwoLocationFlag = true;
13268 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13269 // TwoOrMoreLocationsWarningGiven = true;
13270  }
13271  LastLocationName = AVEntry.LocationName;
13272  LastEntryIsAnArrival = false;
13273  }
13274  else if(AVEntry.FormatType == TimeLoc)
13275  {
13276  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
13277  {
13278  SecondPassMessage(GiveMessages,
13279  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
13280  TrainDataVector.clear();
13281  Utilities->CallLogPop(831);
13282  return(false);
13283  }
13284  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
13285  {
13286  SecondPassMessage(GiveMessages,
13287  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
13289 // TrainDataVector.clear();
13290 // Utilities->CallLogPop(832);
13291 // return false;
13292  }
13293  LastLocationName = AVEntry.LocationName;
13294  LastEntryIsAnArrival = !LastEntryIsAnArrival;
13295  }
13296  }
13297  }
13298  }
13299 
13300  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
13301  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
13302  AnsiString LocationNameToBeChecked = "";
13303 
13304  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13305  {
13306  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13307  unsigned int y = 0;
13308  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13309  // first discard unlocated Snt entries as they don't have location name set
13310  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
13311  {
13312  y = 1;
13313  }
13314  while(y < TDEntry.ActionVector.size())
13315  // need to check each location name separately in turn, skipped for SignallerControl entries
13316  {
13317  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
13318  {
13319  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
13320  }
13321  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
13322  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
13323  {
13324  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
13325  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
13326  {
13327  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
13328  }
13329  if(AVEntry.Command == "cdt")
13330  {
13331  break; // out of the 'z' loop since the check is only valid up to a change of direction
13332  }
13333  if(AVEntry.LocationName == LocationNameToBeChecked)
13334  {
13335  continue; // keep going while name same
13336  }
13337  if(AVEntry.LocationName != LocationNameToBeChecked)
13338  // if name different check forwards to see if repeats
13339  {
13340  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
13341  {
13342  if(TDEntry.ActionVector.at(a).Command == "cdt")
13343  {
13344  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
13345  }
13346  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13347  {
13348  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13349  TwoLocationFlag = true;
13350 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13351 // TwoOrMoreLocationsWarningGiven = true;
13352  }
13353  }
13354  break; // out of the 'z' loop since have checked 'a' as far as need to
13355  }
13356  }
13357  y++;
13358  }
13359  }
13360  if(TwoLocationFlag)
13361  {
13362  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
13363  TwoLocationList.unique(); //remove duplicates
13364  }
13365 
13366  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
13367  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13368  {
13369  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13370  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13371  {
13372  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13373  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
13374  {
13375  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
13376  }
13377  AnsiString LocName = "";
13378  // dummy, only used so can call IsSNTEntryLocated
13379  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
13380  {
13381  if(AVEntry.LocationName == "")
13382  {
13383  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
13384  }
13385  }
13386  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
13387  {
13388  if(AVEntry.LocationName != "")
13389  {
13390  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
13391  }
13392  }
13393  }
13394  }
13395 
13396 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
13397  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
13398  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
13399 
13400  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
13401  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
13402 
13403  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
13404  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
13405  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
13406 */
13407  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
13408  {
13409  // non-shuttles & non-repeating links separately, but don't check that there isn't a
13410  // duplicate between a non-repeating shuttle and another - leave original tests in as
13411  // these also set the pointers
13412  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13413  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13414  {
13415  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13416  if(AVEntry.OtherHeadCode != "")
13417  {
13418  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13419  {
13420  Utilities->CallLogPop(1584);
13421  return(false); // error message given in called function
13422  }
13423  }
13424  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13425  {
13426  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13427  {
13428  Utilities->CallLogPop(1585);
13429  return(false); // error message given in called function
13430  }
13431  }
13432  }
13433  }
13434 
13435  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13436  {
13437  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13438  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13439  {
13440  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13441  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13442  {
13443  if(AVEntry.OtherHeadCode != "")
13444  {
13445  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
13446  // false = non-shuttle
13447  {
13448  Utilities->CallLogPop(864);
13449  return(false); // error message given in called function
13450  }
13451  }
13452  }
13453  }
13454  }
13455 
13456  // now repeat the check just for the shuttles
13457  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13458  {
13459  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13460  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13461  {
13462  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13463  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
13464  {
13465  if(AVEntry.OtherHeadCode != "")
13466  {
13467  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
13468  // true = shuttle
13469  {
13470  Utilities->CallLogPop(1100);
13471  return(false); // error message given in called function
13472  }
13473  }
13474  }
13475  }
13476  }
13477 
13478  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13479  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13480  {
13481  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13482  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13483  {
13484  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13485  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13486  {
13488  {
13489  Utilities->CallLogPop(1060);
13490  return(false); // error message given in called function
13491  }
13492  }
13493  }
13494  }
13495 
13496  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
13497  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
13498  // don't ever need to and as designed would skip repeats
13499  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13500  {
13501  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13502  {
13503  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13504  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
13505  {
13506  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
13507  {
13508  Utilities->CallLogPop(1090);
13509  return(false); // error message given in called function
13510  }
13511  }
13512  }
13513  }
13514 
13515  // check all entries have all types set to something
13516  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13517  {
13518  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13519  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13520  {
13521  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13522  if(AVEntry.FormatType == NoFormat)
13523  {
13524  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
13525  }
13526  else if(AVEntry.SequenceType == NoSequence)
13527  {
13528  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
13529  }
13530  else if(AVEntry.LocationType == NoLocation)
13531  {
13532  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
13533  }
13534  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
13535  {
13536  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
13537  }
13538  }
13539  }
13540 
13541  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
13542  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13543  {
13544  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13545  // non-const reference so can alter content
13546  TTrainOperatingData TData;
13547  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
13548  if(LastAVEntry.FormatType == Repeat) // check if a repeat
13549  {
13550 /*
13551  class TTrainOperatingData
13552  {
13553  public:
13554  int TrainID; - default, set at construction
13555  TActionEventType EventReported; used during operation
13556  TRunningEntry RunningEntry; - default, set at construction
13557  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
13558  };
13559 */
13560  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
13561  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
13562  {
13563  TDEntry.TrainOperatingDataVector.push_back(TData);
13564  }
13565  }
13566  else
13567  {
13568  TDEntry.NumberOfTrains = 1;
13569  }
13570  }
13571 
13572  // check that don't include any Continuation names
13573  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13574  {
13575  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13576  {
13577  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13578  AnsiString HC = TrainDataVector.at(x).HeadCode;
13579  if(LocName != "")
13580  {
13581  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
13582  {
13583  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
13584  TrainDataVector.clear();
13585  Utilities->CallLogPop(1578);
13586  return(false);
13587  }
13588  }
13589  }
13590  }
13591 
13592  // check that all repeat times below 96h
13593  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13594  {
13595  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
13596  int IncMinutes = 0;
13597  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
13598  {
13599  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
13600  }
13601  else
13602  {
13603  continue; // basic times already checked in CheckTimeValidity
13604  }
13605  AnsiString HC = TrainDataVector.at(x).HeadCode;
13606  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13607  {
13608  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
13609  {
13610  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13611  {
13612  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
13613  TrainDataVector.clear();
13614  Utilities->CallLogPop(1818);
13615  return(false);
13616  }
13617  }
13618  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
13619  {
13620  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13621  // 3d 23h 59m = 3.9993055556
13622  {
13623  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13624  TrainDataVector.clear();
13625  Utilities->CallLogPop(1819);
13626  return(false);
13627  }
13628  }
13629  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
13630  {
13631  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13632  // 3d 23h 59m = 3.9993055556
13633  {
13634  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13635  TrainDataVector.clear();
13636  Utilities->CallLogPop(1820);
13637  return(false);
13638  }
13639  }
13640  }
13641  }
13642 
13643  // Now that all set up change any extended headcodes back to ordinary headcodes
13644  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13645  {
13646  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
13647  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13648  {
13649  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
13650  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
13651  }
13652  }
13653 
13654  // SaveTrainDataVectorToFile(0);//test
13656  Utilities->CallLogPop(782);
13657  return(true);
13658 }
13659 
13660 // ---------------------------------------------------------------------------
13661 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13663 {
13664  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13665 }
13666 
13667 // ---------------------------------------------------------------------------
13668 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13670 {
13671  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13672  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13673  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13674 }
13675 
13676 // ---------------------------------------------------------------------------
13677 
13678 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13679 {
13680  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13681  if(HeadCode.Length() > 4) // ignore otherwise
13682  {
13683  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13684  }
13685  Utilities->CallLogPop(1593);
13686 }
13687 
13688 // ---------------------------------------------------------------------------
13689 
13690 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13691 {
13692  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13693  SecondHeadCode);
13694  int ForwardCount = 0;
13695  int ReverseCount = 0;
13696 
13697  if(MainHeadCode == SecondHeadCode)
13698  {
13699  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13700  TrainDataVector.clear();
13701  Utilities->CallLogPop(1594);
13702  return(false);
13703  }
13704  // forward check
13705  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13706  {
13707  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13708  if(TDEntry.HeadCode == MainHeadCode)
13709  {
13710  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13711  {
13712  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13713  if(AVEntry.OtherHeadCode == SecondHeadCode)
13714  {
13715  ForwardCount++;
13716  }
13717  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
13718  // need own check in case both 'Other' & 'NonRepeating' have same headcode
13719  {
13720  ForwardCount++;
13721  }
13722  }
13723  }
13724  }
13725  if(ForwardCount == 0)
13726  // this is an exception because the headcodes are selected in the same order as the forward check
13727  {
13728  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
13729  }
13730  if(ForwardCount > 2)
13731  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
13732  {
13733  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
13734  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13735  TrainDataVector.clear();
13736  Utilities->CallLogPop(1587);
13737  return(false);
13738  }
13739  // reverse check
13740  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13741  {
13742  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13743  if(TDEntry.HeadCode == SecondHeadCode)
13744  {
13745  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13746  {
13747  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13748  if(AVEntry.OtherHeadCode == MainHeadCode)
13749  {
13750  ReverseCount++;
13751  }
13752  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13753  {
13754  ReverseCount++;
13755  }
13756  }
13757  }
13758  }
13759 
13760  if(ReverseCount == 0)
13761  {
13762  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
13763  TrainDataVector.clear();
13764  Utilities->CallLogPop(1588);
13765  return(false);
13766  }
13767  if(ReverseCount > 2)
13768  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
13769  {
13770  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
13771  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13772  TrainDataVector.clear();
13773  Utilities->CallLogPop(1589);
13774  return(false);
13775  }
13776  if(ForwardCount != ReverseCount)
13777  {
13778  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
13779  " than the other way round");
13780  TrainDataVector.clear();
13781  Utilities->CallLogPop(1610);
13782  return(false);
13783  }
13784  Utilities->CallLogPop(1590);
13785  return(true);
13786 }
13787 
13788 // ---------------------------------------------------------------------------
13789 
13790 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
13791 /* Return false for no find or more than one find, check correct types of link
13792  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
13793  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
13794  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
13795  Then do the same in reverse.
13796  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
13797  if main is Fns other must be Sns; if main is jbo other must be Fjo.
13798  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
13799  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
13800  for Sfs & Sns services. Finally check the repeat entries if present are consistent
13801 
13802  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
13803  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
13804 
13805  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
13806 */
13807 
13808 {
13809  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
13810  int ForwardCount = 0;
13811  int ReverseCount = 0;
13812  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13813  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13814  TTrainDataEntry *MainTrainDataPtr = 0;
13815  TTrainDataEntry *OtherTrainDataPtr = 0;
13816 
13817  // forward check
13818  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13819  {
13820  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13821  if(TDEntry.HeadCode == MainHeadCode)
13822  {
13823  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13824  {
13825  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13826  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13827  {
13828  if(AVEntry.OtherHeadCode == OtherHeadCode)
13829  {
13830  MainTrainDataPtr = &TrainDataVector.at(x);
13831  ForwardEntryPtr = &AVEntry;
13832  ForwardCount++;
13833  ForwardTDVectorNumber = x;
13834  }
13835  }
13836  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
13837  (AVEntry.Command == "Frh-sh")))
13838  {
13839  if(AVEntry.OtherHeadCode == OtherHeadCode)
13840  {
13841  MainTrainDataPtr = &TrainDataVector.at(x);
13842  ForwardEntryPtr = &AVEntry;
13843  ForwardCount++;
13844  ForwardTDVectorNumber = x;
13845  }
13846  }
13847  }
13848  }
13849  }
13850  if(ForwardCount == 0)
13851  // this is an exception because the headcodes are selected in the same order as the forward check
13852  {
13853  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
13854  }
13855  if(ForwardCount > 1)
13856  {
13857  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
13858  MainHeadCode);
13859  TrainDataVector.clear();
13860  Utilities->CallLogPop(836);
13861  return(false);
13862  }
13863  // reverse check
13864  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13865  {
13866  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13867  if(TDEntry.HeadCode == OtherHeadCode)
13868  {
13869  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13870  {
13871  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13872  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13873  {
13874  if(AVEntry.OtherHeadCode == MainHeadCode)
13875  {
13876  OtherTrainDataPtr = &TrainDataVector.at(x);
13877  ReverseCount++;
13878  ReverseEntryPtr = &AVEntry;
13879  ReverseTDVectorNumber = x;
13880  }
13881  }
13882  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
13883  {
13884  if(AVEntry.OtherHeadCode == MainHeadCode)
13885  {
13886  OtherTrainDataPtr = &TrainDataVector.at(x);
13887  ReverseCount++;
13888  ReverseEntryPtr = &AVEntry;
13889  ReverseTDVectorNumber = x;
13890  }
13891  }
13892  }
13893  }
13894  }
13895 
13896  if(ReverseCount == 0)
13897  {
13898  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
13899  TrainDataVector.clear();
13900  Utilities->CallLogPop(837);
13901  return(false);
13902  }
13903  if(ReverseCount > 1)
13904  {
13905  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13906  OtherHeadCode);
13907  TrainDataVector.clear();
13908  Utilities->CallLogPop(838);
13909  return(false);
13910  }
13911  // these will all be false for !Shuttle
13912  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
13913  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
13914  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
13915  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
13916 
13917  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
13918  {
13919  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
13920  TrainDataVector.clear();
13921  Utilities->CallLogPop(1058);
13922  return(false);
13923  }
13924  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
13925  {
13926  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
13927  TrainDataVector.clear();
13928  Utilities->CallLogPop(1059);
13929  return(false);
13930  }
13931  if(ForwardEntryPtr->LocationName == "")
13932  {
13933  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13934  ". One or other service does not have a location set");
13935  TrainDataVector.clear();
13936  Utilities->CallLogPop(526);
13937  return(false);
13938  }
13939  if(ReverseEntryPtr->LocationName == "")
13940  {
13941  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13942  ". One or other service does not have a location set");
13943  TrainDataVector.clear();
13944  Utilities->CallLogPop(527);
13945  return(false);
13946  }
13947  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13948  {
13949  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13950  " is at a different location to the referencing train " + MainHeadCode);
13951  TrainDataVector.clear();
13952  Utilities->CallLogPop(842);
13953  return(false);
13954  }
13955  // ignore shuttle repeat links for first time check
13956  if(!Shuttle)
13957  {
13958  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13959  {
13960  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13961  " has a different event time to the referencing train " + MainHeadCode);
13962  TrainDataVector.clear();
13963  Utilities->CallLogPop(525);
13964  return(false);
13965  }
13966  }
13967  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
13968  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
13969  if(ForwardShuttleStart && ReverseShuttleFinish)
13970  // Shuttle must be true if these are true
13971  {
13972  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
13973  {
13974  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
13975  " first repeat restart time not consistent with finish service " + OtherHeadCode);
13976  TrainDataVector.clear();
13977  Utilities->CallLogPop(1055);
13978  return(false);
13979  }
13980  }
13981  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
13982  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
13983  {
13984  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13985  {
13986  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
13987  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13988  TrainDataVector.clear();
13989  Utilities->CallLogPop(528);
13990  return(false);
13991  }
13992  }
13993  if(ReverseEntryPtr->Command == "Fjo")
13994  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
13995  {
13996  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13997  {
13998  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
13999  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
14000  TrainDataVector.clear();
14001  Utilities->CallLogPop(862);
14002  return(false);
14003  }
14004  }
14005  if(ReverseEntryPtr->Command == "Fns")
14006  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
14007  {
14008  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14009  {
14010  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
14011  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
14012  TrainDataVector.clear();
14013  Utilities->CallLogPop(529);
14014  return(false);
14015  }
14016  }
14017  if(ForwardEntryPtr->Command == "Sfs")
14018  {
14019  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
14020  {
14021  SecondPassMessage(GiveMessages,
14022  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
14023  MainHeadCode);
14024  TrainDataVector.clear();
14025  Utilities->CallLogPop(530);
14026  return(false);
14027  }
14028  }
14029  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
14030  {
14031  if(ReverseEntryPtr->Command != "Sfs")
14032  {
14033  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
14034  MainHeadCode);
14035  TrainDataVector.clear();
14036  Utilities->CallLogPop(839);
14037  return(false);
14038  }
14039  else
14040  {
14041  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
14042  {
14043  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
14044  TrainDataVector.clear();
14045  Utilities->CallLogPop(849);
14046  return(false);
14047  }
14048  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
14049  {
14050  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
14051  TrainDataVector.clear();
14052  Utilities->CallLogPop(850);
14053  return(false);
14054  }
14055  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
14056  {
14057  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
14058  TrainDataVector.clear();
14059  Utilities->CallLogPop(846);
14060  return(false);
14061  }
14062  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14063  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14064  if(OtherTrainDataPtr->Description == "")
14065  {
14066  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14067  }
14068  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
14069  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14070  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14071  }
14072  }
14073  if(ForwardEntryPtr->Command == "Sns")
14074  {
14075  if(ReverseEntryPtr->Command != "Fns")
14076  {
14077  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
14078  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
14079  TrainDataVector.clear();
14080  Utilities->CallLogPop(531);
14081  return(false);
14082  }
14083  }
14084  if(ForwardEntryPtr->Command == "Fns")
14085  {
14086  if(ReverseEntryPtr->Command != "Sns")
14087  {
14088  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
14089  " and forms a new service with headcode " + OtherHeadCode);
14090  TrainDataVector.clear();
14091  Utilities->CallLogPop(840);
14092  return(false);
14093  }
14094  else
14095  {
14096  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14097  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14098  if(OtherTrainDataPtr->Description == "")
14099  {
14100  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14101  }
14102  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14103  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14104  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14105  }
14106  }
14107  if(ForwardEntryPtr->Command == "jbo")
14108  {
14109  if(ReverseEntryPtr->Command != "Fjo")
14110  {
14111  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
14112  " and is joined by a train with headcode " + OtherHeadCode);
14113  TrainDataVector.clear();
14114  Utilities->CallLogPop(841);
14115  return(false);
14116  }
14117  }
14118  if(ForwardEntryPtr->Command == "Fjo")
14119  {
14120  if(ReverseEntryPtr->Command != "jbo")
14121  {
14122  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
14123  " and joins a train with headcode " + OtherHeadCode);
14124  TrainDataVector.clear();
14125  Utilities->CallLogPop(532);
14126  return(false);
14127  }
14128  else
14129  {
14130  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14131  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14132  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
14133  {
14134  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14135  }
14136  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
14137  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
14138  }
14139  }
14140  if(ForwardShuttleStart)
14141  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
14142  {
14143  if(!ReverseShuttleFinish)
14144  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
14145  {
14146  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
14147  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
14148  TrainDataVector.clear();
14149  Utilities->CallLogPop(1056);
14150  return(false);
14151  }
14152  }
14153  if(ReverseShuttleStart)
14154  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
14155  {
14156  if(!ForwardShuttleFinish)
14157  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
14158  {
14159  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
14160  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
14161  TrainDataVector.clear();
14162  Utilities->CallLogPop(1057);
14163  return(false);
14164  }
14165  else
14166  {
14167  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14168  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14169 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
14170  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
14171 */
14172  }
14173  }
14174  // check repeat information consistent if present
14175  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
14176  // and those not accessed here
14177 
14178  // still need to check the non-repeating links and that they have no repeats - do that outside this function
14179  bool MainRepeat = false, OtherRepeat = false;
14180  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
14181 
14182  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
14183  {
14184  MainRepeat = true;
14185  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
14186  }
14187  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
14188  {
14189  OtherRepeat = true;
14190  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
14191  }
14192  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
14193  {
14194  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
14195  " and the associated train with headcode " + OtherHeadCode);
14196  TrainDataVector.clear();
14197  Utilities->CallLogPop(844);
14198  return(false);
14199  }
14200  if(MainRepeat && OtherRepeat)
14201  {
14202  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
14203  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
14204  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
14205  {
14206  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
14207  " and the associated train with headcode " + OtherHeadCode);
14208  TrainDataVector.clear();
14209  Utilities->CallLogPop(845);
14210  return(false);
14211  }
14212  }
14213  Utilities->CallLogPop(863);
14214  return(true);
14215 }
14216 
14217 // ---------------------------------------------------------------------------
14218 
14219 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
14220 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
14221 {
14222  // strip spaces from extreme ends of input
14223  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
14224  if(Input == "")
14225  {
14226  Utilities->CallLogPop(856);
14227  return;
14228  }
14229  while(Input[1] == ' ')
14230  {
14231  if(Input.Length() > 1)
14232  {
14233  Input = Input.SubString(2, Input.Length() - 1);
14234  }
14235  else
14236  {
14237  Input = "";
14238  Utilities->CallLogPop(857);
14239  return;
14240  }
14241  }
14242  if(Input == "")
14243  {
14244  Utilities->CallLogPop(858);
14245  return;
14246  }
14247  while(Input[Input.Length()] == ' ')
14248  {
14249  if(Input.Length() > 1)
14250  {
14251  Input = Input.SubString(1, Input.Length() - 1);
14252  }
14253  else
14254  {
14255  Input = "";
14256  Utilities->CallLogPop(859);
14257  return;
14258  }
14259  }
14260  // now strip spaces immediately after all commas and semicolons within the text
14261  AnsiString Output = "";
14262  bool DelimiterFound = false;
14263 
14264  for(int x = 1; x < Input.Length() + 1; x++)
14265  {
14266  if(DelimiterFound)
14267  {
14268  if(Input[x] == ' ')
14269  {
14270  continue;
14271  }
14272  }
14273  if((Input[x] != ',') && (Input[x] != ';'))
14274  {
14275  DelimiterFound = false;
14276  Output = Output + Input[x];
14277  }
14278  else
14279  {
14280  DelimiterFound = true;
14281  Output = Output + Input[x];
14282  }
14283  }
14284  if(Output == "")
14285  {
14286  Input = "";
14287  Utilities->CallLogPop(860);
14288  return;
14289  }
14290  // now strip spaces immediately before all commas and semicolons within the text
14291  Input = Output;
14292  Output = "";
14293  DelimiterFound = false;
14294  for(int x = Input.Length(); x > 0; x--)
14295  {
14296  if(DelimiterFound)
14297  {
14298  if(Input[x] == ' ')
14299  {
14300  continue;
14301  }
14302  }
14303  if((Input[x] != ',') && (Input[x] != ';'))
14304  {
14305  DelimiterFound = false;
14306  Output = AnsiString(Input[x]) + Output;
14307  }
14308  else
14309  {
14310  DelimiterFound = true;
14311  Output = AnsiString(Input[x]) + Output;
14312  }
14313  }
14314  Input = Output;
14315  Utilities->CallLogPop(861);
14316 }
14317 
14318 // ---------------------------------------------------------------------------
14319 
14320 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
14321 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
14322 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
14323 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
14324 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
14325 // are done in this function, they must be done elsewhere.
14326 //a starting speed > 0 always returns false
14327 {
14328  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
14329  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
14330  LocationName = "";
14331  if(TDEntry.StartSpeed > 0)
14332  {
14333  Utilities->CallLogPop(1784);
14334  return(false);
14335  }
14336  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
14337  {
14338  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
14339  }
14341  {
14342  Utilities->CallLogPop(852);
14343  return(false);
14344  }
14345  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
14346  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
14347 
14348  if(LocRear != "")
14349  {
14350  LocationName = LocRear;
14351  }
14352  else
14353  {
14354  LocationName = LocFront;
14355  }
14356  if(LocationName == "")
14357  {
14358  Utilities->CallLogPop(1036);
14359  return(false);
14360  }
14361  if(AVEntry0.SignallerControl)
14362  {
14363  Utilities->CallLogPop(1773);
14364  return(true);
14365  }
14366 // here if not a signaller start entry so must be at least one more entry, and it is at a location
14367 
14368 //Ok Not ok continue
14369 
14370 //Frh if Snt Frh-sh cdt
14371 //Fns if Snt Fns-sh fsp or rsp
14372 //Fjo if Snt TimeTimeLoc jbo
14373 //F-nshs if Snt pas
14374 //TimeLoc dep Fer
14375 // TimeLoc arr
14376 
14377  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
14378  {
14379  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
14380  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
14381  {
14382  Utilities->CallLogPop(1037);
14383  return(true);
14384  }
14385  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
14386  {
14387  Utilities->CallLogPop(2442);
14388  return(true);
14389  }
14390  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
14391  {
14392  Utilities->CallLogPop(2438);
14393  return(false);
14394  }
14395  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
14396  {
14397  Utilities->CallLogPop(854);
14398  return(false);
14399  }
14400  if((AVEntry.Command == "cdt") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
14401  {
14402  continue;
14403  }
14404  }
14405  Utilities->CallLogPop(855);
14406  return(false);
14407 
14408 }
14409 
14410 // ---------------------------------------------------------------------------
14411 
14412 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
14413 {
14414  // checks that the new train start elements are valid - both exist & are connected, and that not
14415  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
14416  // & not starting with front on a continuation
14417  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
14418  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
14419 
14420  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
14421  if(RearPosition < 0)
14422  // error message given in GetTrackVectorPositionFromString
14423  {
14424  Utilities->CallLogPop(759);
14425  return(false);
14426  }
14427  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
14428  if(FrontPosition < 0)
14429  // error message given in GetTrackVectorPositionFromString
14430  {
14431  Utilities->CallLogPop(760);
14432  return(false);
14433  }
14434  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
14435  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
14436  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
14437 
14438  // check front & rear connected
14439  for(int x = 0; x < 4; x++)
14440  {
14441  if(RearTrackElement.Conn[x] == FrontPosition)
14442  {
14443  RearExitPos = x;
14444  break;
14445  }
14446  if(x == 3)
14447  {
14448  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
14449  Utilities->CallLogPop(762);
14450  return(false);
14451  }
14452  }
14453  // check not starting with front on a continuation
14454  if(FrontType == Continuation)
14455  {
14456  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
14457  Utilities->CallLogPop(937);
14458  return(false);
14459  }
14460  // check not starting on a level crossing
14461  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
14462  {
14463  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
14464  Utilities->CallLogPop(1951);
14465  return(false);
14466  }
14467  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
14468  {
14469  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
14470  Utilities->CallLogPop(1952);
14471  return(false);
14472  }
14473  // check if trying to start on diverging leg of points
14474  if((RearType == Points) && (RearExitPos == 3))
14475  {
14476  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
14477  Utilities->CallLogPop(936);
14478  return(false);
14479  }
14480  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
14481  {
14482  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
14483  Utilities->CallLogPop(1808);
14484  return(false);
14485  }
14486  Utilities->CallLogPop(905);
14487  return(true);
14488 }
14489 
14490 // ---------------------------------------------------------------------------
14491 
14492 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
14493 // Rear & front element validity already checked in CheckStartPositionValidity
14494 // This checks for points in correct orientation, no train at start position and not starting on a locked route
14495 {
14496  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
14497  AnsiString(RearExitPos));
14498  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
14499 
14500  if(RearTrackElement.TrackType == Continuation)
14501  {
14502  EventType = FailTrainEntry;
14503  }
14504  else
14505  {
14506  EventType = FailCreateTrain;
14507  }
14508  int FrontPosition = RearTrackElement.Conn[RearExitPos];
14509  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
14510  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
14511  TTrackType RearType = RearTrackElement.TrackType;
14512  TTrackType FrontType = FrontTrackElement.TrackType;
14513  AnsiString RearName, FrontName;
14514 
14515  if(RearTrackElement.ActiveTrackElementName != "")
14516  {
14517  RearName = RearTrackElement.ActiveTrackElementName;
14518  }
14519  else
14520  {
14521  RearName = RearTrackElement.ElementID;
14522  }
14523  if(FrontTrackElement.ActiveTrackElementName != "")
14524  {
14525  FrontName = FrontTrackElement.ActiveTrackElementName;
14526  }
14527  else
14528  {
14529  FrontName = FrontTrackElement.ElementID;
14530  }
14531  TPrefDirElement PrefDirElement; // needed for next function but not used
14532  int LockedVectorNumber; // needed for next function but not used
14533 
14534  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
14535  {
14536  if(ReportFlag)
14537  {
14538  if(EventType == FailCreateTrain)
14539  {
14540  EventType = FailCreateLockedRoute;
14541  }
14542  else
14543  {
14544  EventType = FailEnterLockedRoute;
14545  }
14546  LogActionError(47, HeadCode, "", EventType, FrontName);
14547  }
14548  Utilities->CallLogPop(940);
14549  return(false);
14550  }
14551  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
14552  {
14553  if(ReportFlag)
14554  {
14555  if(EventType == FailCreateTrain)
14556  {
14557  EventType = FailCreateLockedRoute;
14558  }
14559  else
14560  {
14561  EventType = FailEnterLockedRoute;
14562  }
14563  LogActionError(48, HeadCode, "", EventType, RearName);
14564  }
14565  Utilities->CallLogPop(1809);
14566  return(false);
14567  }
14568  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
14569  {
14570  if(ReportFlag)
14571  {
14572  LogActionError(27, HeadCode, "", EventType, RearName);
14573  }
14574  Utilities->CallLogPop(1810);
14575  return(false);
14576  }
14577  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
14578  {
14579  if(ReportFlag)
14580  {
14581  if(EventType == FailCreateTrain)
14582  {
14583  LogActionError(28, HeadCode, "", EventType, FrontName);
14584  }
14585  else
14586  {
14587  LogActionError(43, HeadCode, "", EventType, RearName);
14588  }
14589  }
14590  Utilities->CallLogPop(941);
14591  return(false);
14592  }
14593  if(RearType == Bridge)
14594  {
14595  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14596  {
14597  if(ReportFlag)
14598  {
14599  LogActionError(29, HeadCode, "", EventType, RearName);
14600  }
14601  Utilities->CallLogPop(942);
14602  return(false);
14603  }
14604  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14605  {
14606  if(ReportFlag)
14607  {
14608  LogActionError(30, HeadCode, "", EventType, RearName);
14609  }
14610  Utilities->CallLogPop(943);
14611  return(false);
14612  }
14613  }
14614  if(FrontType == Bridge)
14615  {
14616  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14617  {
14618  if(ReportFlag)
14619  {
14620  if(EventType == FailCreateTrain)
14621  {
14622  LogActionError(31, HeadCode, "", EventType, FrontName);
14623  }
14624  else
14625  {
14626  LogActionError(44, HeadCode, "", EventType, RearName);
14627  }
14628  }
14629  Utilities->CallLogPop(944);
14630  return(false);
14631  }
14632  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14633  {
14634  if(ReportFlag)
14635  {
14636  if(EventType == FailCreateTrain)
14637  {
14638  LogActionError(45, HeadCode, "", EventType, FrontName);
14639  }
14640  else
14641  {
14642  LogActionError(46, HeadCode, "", EventType, RearName);
14643  }
14644  }
14645  Utilities->CallLogPop(945);
14646  return(false);
14647  }
14648  }
14649  EventType = FailCreatePoints;
14650  if(RearType == Points)
14651  {
14652  if(RearTrackElement.Attribute == 1)
14653  {
14654  if(ReportFlag)
14655  {
14656  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
14657  }
14658  Utilities->CallLogPop(933);
14659  return(false);
14660  }
14661  }
14662  if(FrontType == Points)
14663  {
14664  if(FrontTrackElement.Attribute == 1)
14665  {
14666  if(ReportFlag)
14667  {
14668  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14669  }
14670  Utilities->CallLogPop(934);
14671  return(false);
14672  }
14673  }
14674 
14675  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
14676  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
14677  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
14678  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
14679  int RouteNumber; //not used
14680  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
14681  {
14682  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
14683  {
14684  EventType = FailEntryRouteSetAgainst;
14685  if(ReportFlag)
14686  {
14687  LogActionError(63, HeadCode, "", EventType, RearName);
14688  }
14689  Utilities->CallLogPop(2317);
14690  return(false);
14691  }
14692  }
14693  Utilities->CallLogPop(939);
14694  return(true);
14695 }
14696 
14697 // ---------------------------------------------------------------------------
14698 
14699 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14700 {
14701  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14702  "," + AnsiString(IncDigits));
14703  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
14704  {
14705  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
14706  }
14707  if(!Last2CharactersBothDigits(2, BaseHeadCode))
14708  {
14709  Utilities->CallLogPop(1893);
14710  return(BaseHeadCode);
14711  }
14712  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
14713  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
14714 
14715  while(NextRepeatDigits >= 100)
14716  {
14717  NextRepeatDigits -= 100; // rolls over after 99
14718  }
14719  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
14720 
14721  if(NextRepeatDigitsStr.Length() < 2)
14722  {
14723  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
14724  }
14725  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
14726 
14727  Utilities->CallLogPop(1365);
14728  return(NextRepeatHeadCode);
14729 }
14730 
14731 // ---------------------------------------------------------------------------
14732 
14733 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
14734 {
14735  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
14736  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
14737  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
14738  Utilities->CallLogPop(1366);
14739  return(NextRepeatTime);
14740 }
14741 
14742 // ---------------------------------------------------------------------------
14743 
14744 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
14745 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
14746 {
14747  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
14748  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
14749  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14750  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14751  int RepeatSecs = RepeatMinutes * 60;
14752 
14753  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
14754  {
14755  Utilities->CallLogPop(1367);
14756  return(false);
14757  }
14758  else
14759  {
14760  Utilities->CallLogPop(1368);
14761  return(true);
14762  }
14763 }
14764 
14765 // ---------------------------------------------------------------------------
14766 
14767 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
14768 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14769 
14770 /* Double crosslink (shuttle) table:
14771 
14772 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
14773  Code ShuttleLink- EntryPtr ShuttleLink-
14774  HeadCode EntryPtr
14775 
14776 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
14777 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
14778 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
14779 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
14780 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
14781 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
14782 
14783 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
14784 */
14785 
14786 {
14787  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
14788  NonRepeatingHeadCode);
14789  int ForwardCount = 0;
14790  int ReverseCount = 0;
14791  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14792  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14793  // Forward corresponds to Main, Reverse to Other
14794  TTrainDataEntry *MainTrainDataPtr = 0;
14795  TTrainDataEntry *OtherTrainDataPtr = 0;
14796 
14797  // forward check
14798  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14799  {
14800  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14801  if(TDEntry.HeadCode == MainHeadCode)
14802  {
14803  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14804  {
14805  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14806  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
14807  {
14808  MainTrainDataPtr = &TrainDataVector.at(x);
14809  ForwardEntryPtr = &AVEntry;
14810  ForwardCount++;
14811  ForwardTDVectorNumber = x;
14812  }
14813  }
14814  }
14815  }
14816  if(ForwardCount == 0)
14817  // this is an exception because the headcodes are selected in the same order as the forward check
14818  {
14819  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
14820  }
14821  if(ForwardCount > 1)
14822  {
14823  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
14824  MainHeadCode);
14825  TrainDataVector.clear();
14826  Utilities->CallLogPop(1061);
14827  return(false);
14828  }
14829  // reverse check
14830  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14831  {
14832  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14833  if(TDEntry.HeadCode == NonRepeatingHeadCode)
14834  {
14835  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14836  {
14837  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14838  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14839  {
14840  OtherTrainDataPtr = &TrainDataVector.at(x);
14841  ReverseCount++;
14842  ReverseEntryPtr = &AVEntry;
14843  ReverseTDVectorNumber = x;
14844  }
14845  }
14846  }
14847  }
14848 
14849  if(ReverseCount == 0)
14850  {
14851  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
14852  TrainDataVector.clear();
14853  Utilities->CallLogPop(1062);
14854  return(false);
14855  }
14856  if(ReverseCount > 1)
14857  {
14858  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14859  NonRepeatingHeadCode);
14860  TrainDataVector.clear();
14861  Utilities->CallLogPop(1063);
14862  return(false);
14863  }
14864  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
14865  {
14866  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
14867  TrainDataVector.clear();
14868  Utilities->CallLogPop(1064);
14869  return(false);
14870  }
14871  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
14872  {
14873  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
14874  TrainDataVector.clear();
14875  Utilities->CallLogPop(1065);
14876  return(false);
14877  }
14878  if(ForwardEntryPtr->LocationName == "")
14879  {
14880  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14881  ". One or other service does not have a location set");
14882  TrainDataVector.clear();
14883  Utilities->CallLogPop(1066);
14884  return(false);
14885  }
14886  if(ReverseEntryPtr->LocationName == "")
14887  {
14888  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14889  ". One or other service does not have a location set");
14890  TrainDataVector.clear();
14891  Utilities->CallLogPop(1067);
14892  return(false);
14893  }
14894  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14895  {
14896  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
14897  " is at a different location to the referencing train " + MainHeadCode);
14898  TrainDataVector.clear();
14899  Utilities->CallLogPop(1068);
14900  return(false);
14901  }
14902  if(ForwardEntryPtr->Command == "F-nshs")
14903  // i.e. the non repeating link into the shuttle service
14904  {
14905  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14906  {
14907  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
14908  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
14909  TrainDataVector.clear();
14910  Utilities->CallLogPop(1069);
14911  return(false);
14912  }
14913  }
14914  if(ForwardEntryPtr->Command == "Fns-sh")
14915  // i.e. the non repeating link out from the shuttle service
14916  {
14917  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
14918  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
14919  {
14920  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
14921  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
14922  TrainDataVector.clear();
14923  Utilities->CallLogPop(1070);
14924  return(false);
14925  }
14926  }
14927  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
14928  // i.e. a non repeating link to or from the shuttle service
14929  {
14930  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14931  {
14932  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
14933  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
14934  TrainDataVector.clear();
14935  Utilities->CallLogPop(1071);
14936  return(false);
14937  }
14938  }
14939 /* it's allowed to have a different description
14940  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
14941  {
14942  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
14943  {
14944  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
14945  TrainDataVector.clear();
14946  Utilities->CallLogPop(1072);
14947  return false;
14948  }
14949  }
14950 */
14951  if(ForwardEntryPtr->Command == "Sns-sh")
14952  {
14953  if(ReverseEntryPtr->Command != "F-nshs")
14954  {
14955  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
14956  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
14957  TrainDataVector.clear();
14958  Utilities->CallLogPop(1073);
14959  return(false);
14960  }
14961  }
14962  if(ForwardEntryPtr->Command == "F-nshs")
14963  {
14964  if(ReverseEntryPtr->Command != "Sns-sh")
14965  {
14966  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
14967  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
14968  TrainDataVector.clear();
14969  Utilities->CallLogPop(1074);
14970  return(false);
14971  }
14972  else
14973  {
14974  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14975  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
14976  if(OtherTrainDataPtr->Description == "")
14977  {
14978  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14979  }
14980  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14981  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14982  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14983  }
14984  }
14985  if(ForwardEntryPtr->Command == "Sns-fsh")
14986  {
14987  if(ReverseEntryPtr->Command != "Fns-sh")
14988  {
14989  SecondPassMessage(GiveMessages,
14990  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
14991  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
14992  TrainDataVector.clear();
14993  Utilities->CallLogPop(1075);
14994  return(false);
14995  }
14996  }
14997  if(ForwardEntryPtr->Command == "Fns-sh")
14998  {
14999  if(ReverseEntryPtr->Command != "Sns-fsh")
15000  {
15001  SecondPassMessage(GiveMessages,
15002  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
15003  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
15004  TrainDataVector.clear();
15005  Utilities->CallLogPop(1076);
15006  return(false);
15007  }
15008  else
15009  {
15010  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
15011  // links to the non-repeating non-shuttle linked service
15012  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15013  // needed for creating formatted timetable
15014  if(OtherTrainDataPtr->Description == "")
15015  {
15016  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
15017  }
15018  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15019  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15020  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15021  }
15022  }
15023  Utilities->CallLogPop(1077);
15024  return(true);
15025 }
15026 
15027 // ---------------------------------------------------------------------------
15028 
15029 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
15030 // Forward train is the finish shuttle entry 'Fns-sh'.
15031 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
15032 {
15033  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
15034  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
15035  int ForwardSecs = int(double(ForwardEventTime) * 86400);
15036  int ReverseSecs = int(double(ReverseEventTime) * 86400);
15037  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
15038 
15039  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
15040  {
15041  Utilities->CallLogPop(1369);
15042  return(false);
15043  }
15044  else
15045  {
15046  Utilities->CallLogPop(1370);
15047  return(true);
15048  }
15049 }
15050 
15051 // ---------------------------------------------------------------------------
15052 
15053 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
15054 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
15055 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
15056 // don't ever need to and as designed would skip repeats.
15057 
15058 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
15059 {
15060  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
15061  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
15062  {
15063  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
15064  }
15065  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
15066  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
15067  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
15068 
15069  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
15070  {
15071  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
15072  TrainDataVector.clear();
15073  Utilities->CallLogPop(1091);
15074  return(false);
15075  }
15076  while(LastActionCommand == "Fns")
15077  {
15078  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
15079  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
15080  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
15081  {
15082  SecondPassMessage(GiveMessages,
15083  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
15084  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
15085  TrainDataVector.clear();
15086  Utilities->CallLogPop(1092);
15087  return(false);
15088  }
15089  }
15090  // exit the 'while' with LastActionCommand FSH-XX
15091  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
15092  {
15093  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
15094  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
15095  ". The linking of two or more shuttles is not permitted.");
15096  TrainDataVector.clear();
15097  Utilities->CallLogPop(1093);
15098  return(false);
15099  }
15100  Utilities->CallLogPop(1094);
15101  return(true);
15102 }
15103 
15104 // ---------------------------------------------------------------------------
15105 
15106 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
15107 {
15108  if(!GiveMessages)
15109  {
15110  return;
15111  }
15112  // if(ServiceReference == "") ShowMessage(Message);
15113  if(!CheckHeadCodeValidity(12, false, ServiceReference))
15114  {
15115  ShowMessage(Message);
15116  }
15117  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
15118  // false means don't give messages within the function
15119  else
15120  {
15121  ShowMessage("Service " + ServiceReference + ": " + Message);
15122  }
15123 }
15124 
15125 // ---------------------------------------------------------------------------
15126 
15127 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
15128 {
15129  if(!GiveMessages)
15130  {
15131  return;
15132  }
15133  ShowMessage(Message);
15134 }
15135 
15136 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
15137 // ---------------------------------------------------------------------------
15138 
15139 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
15140 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
15141 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
15142 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
15143 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
15144 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
15145 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
15146 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
15147 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
15148 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
15149 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
15150 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
15151 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
15152 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
15153 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
15154 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
15155 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
15156 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
15157 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
15158 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
15159 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
15160 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
15161 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
15162 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
15163 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
15164 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
15165 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
15166 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
15167 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
15168 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
15169 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5 //added at v2.9.1
15170 {
15171  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
15172  AnsiString(ActionEventType) + "," + LocationID);
15173  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
15174 
15175  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
15176  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + " " + HeadCode; //added at v2.9.1 to give more info to user
15177 
15178  Prefix = " ERROR: ";
15179  if(ActionEventType == FailTrainEntry)
15180  {
15181  Prefix = " HELD: ";
15182  ErrorLog = " can't enter railway, train obstructing entry position ";
15183  WarningStr = " can't enter railway, train obstructing entry position ";
15184  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
15185  }
15186  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
15187  {
15188  Prefix = " HELD: ";
15189  ErrorLog = " can't enter railway, route set against it at entry position ";
15190  WarningStr = " can't enter railway, route set against it at entry position ";
15191  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
15192  }
15193  else if(ActionEventType == FailCreateTrain)
15194  {
15195  Prefix = " HELD: ";
15196  ErrorLog = " can't be created, train obstructing ";
15197  WarningStr = " can't be created, train obstructing ";
15198  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
15199  }
15200  else if(ActionEventType == FailCreateLockedRoute)
15201  {
15202  Prefix = " HELD: ";
15203  ErrorLog = " can't be created on a locked route at ";
15204  WarningStr = " can't be created on a locked route at ";
15205  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
15206  }
15207  else if(ActionEventType == FailEnterLockedRoute)
15208  {
15209  Prefix = " HELD: ";
15210  ErrorLog = " can't enter on a locked route at ";
15211  WarningStr = " can't enter on a locked route at ";
15212  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
15213  }
15214  else if(ActionEventType == FailCreatePoints)
15215  {
15216  Prefix = " HELD: ";
15217  ErrorLog = " can't be created, diverging points at ";
15218  WarningStr = " can't be created, diverging points at ";
15219  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
15220  }
15221  else if(ActionEventType == FailUnexpectedExitRailway)
15222  {
15223  ErrorLog = " left railway unexpectedly at ";
15224  UnexpectedExits++;
15225  }
15226  else if(ActionEventType == FailIncorrectExit)
15227  {
15228  ErrorLog = " left railway at an incorrect exit at ";
15229  IncorrectExits++;
15230  }
15231  else if(ActionEventType == FailLocTooShort)
15232  {
15233  ErrorLog = " failed to split - location too short at ";
15234  WarningStr = " failed to split, location too short at ";
15235  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
15236  }
15237  else if(ActionEventType == FailSplitDueToOtherTrain)
15238  {
15239  Prefix = " HELD: ";
15240  ErrorLog = " unable to split - other train obstructing at ";
15241  WarningStr = " unable to split - other train obstructing at ";
15242  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
15243  }
15244  else if(ActionEventType == FailUnexpectedBuffers)
15245  {
15246  ErrorLog = " stopped at buffers unexpectedly at position ";
15247  }
15248  else if(ActionEventType == FailMissedArrival)
15249  {
15250  ErrorLog = " failed to stop at ";
15251  MissedStops++;
15252  }
15253  else if(ActionEventType == FailMissedSplit)
15254  {
15255  ErrorLog = " failed to split at ";
15257  }
15258  else if(ActionEventType == FailMissedJBO)
15259  {
15260  ErrorLog = " failed to be joined by other train at ";
15262  }
15263  else if(ActionEventType == FailMissedJoinOther)
15264  {
15265  ErrorLog = " failed to join other train at ";
15267  }
15268  else if(ActionEventType == FailMissedTerminate)
15269  {
15270  ErrorLog = " failed to terminate at ";
15272  }
15273  else if(ActionEventType == FailMissedNewService)
15274  {
15275  ErrorLog = " failed to form new service at ";
15277  }
15278  else if(ActionEventType == FailMissedExitRailway)
15279  {
15280  ErrorLog = " failed to exit railway ";
15282  }
15283  else if(ActionEventType == FailMissedChangeDirection)
15284  {
15285  ErrorLog = " failed to change direction at ";
15286 // OtherMissedEvents++; //dropped at v2.12.0 as cdt shouldn't count
15287  }
15288  else if(ActionEventType == FailMissedPass)
15289  {
15290  ErrorLog = " failed to pass ";
15291 // OtherMissedEvents++; //dropped at v2.12.0 as missed pass shouldn't count
15292  }
15293  else if(ActionEventType == FailBuffersPreventingStart)
15294  {
15295  ErrorLog = " facing buffers and unable to start at ";
15296  }
15297  else if(ActionEventType == FailDerailed)
15298  {
15299  ErrorLog = " DERAILED at position ";
15300  Prefix = " DERAILMENT: ";
15301  Derailments++;
15302  }
15303  else if(ActionEventType == FailBufferCrash)
15304  {
15305  ErrorLog = " CRASHED INTO BUFFERS at ";
15306  Prefix = " CRASH: ";
15307  CrashedTrains++;
15308  }
15309  else if(ActionEventType == FailLevelCrossingCrash)
15310  {
15311  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
15312  Prefix = " CRASH: ";
15313  CrashedTrains++;
15314  }
15315  else if(ActionEventType == FailCrashed)
15316  {
15317  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
15318  Prefix = " CRASH: ";
15319  CrashedTrains++;
15320  CrashedTrains++;
15321  }
15322  else if(ActionEventType == FailSPAD)
15323  {
15324  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
15325  Prefix = " SPAD: ";
15326  SPADEvents++;
15327  }
15328  else if(ActionEventType == FailLockedRoute)
15329  {
15330  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
15331  Prefix = " SPAD RISK: ";
15332  SPADRisks++;
15333  }
15334  else if(ActionEventType == RouteForceCancelled)
15335  {
15336  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
15337  }
15338  else if(ActionEventType == WaitingForJBO)
15339  {
15340  Prefix = " WARNING: ";
15341  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
15342  WarningStr = " waiting to join " + OtherHeadCode + " at ";
15343  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
15344  }
15345  else if(ActionEventType == WaitingForFJO)
15346  {
15347  Prefix = " WARNING: ";
15348  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
15349  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
15350  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
15351  }
15352 
15353  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
15354  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
15355  Utilities->CallLogPop(1371);
15356 }
15357 
15358 // ---------------------------------------------------------------------------
15359 
15361 {
15362 /*
15363  TrainDataEntry
15364  AnsiString HeadCode, Description;//null on creation
15365  int StartSpeed, MaxRunningSpeed;//both kph
15366  int RepeatNumber;
15367  TActionVector ActionVector;
15368  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
15369  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
15370 
15371  ActionVectorEntry
15372  TTimetableEntryType FormatType;
15373  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
15374  AnsiString LocationName, Command, OtherHeadCode;//null on creation
15375  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
15376  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15377  int RepeatNumber;
15378 
15379  TrainOperatingData
15380  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
15381  int TrainID;
15382  TRunningEntry RunningEntry;
15383  TDateTime StartTime;
15384  AnsiString HeadCode;
15385 */
15386  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
15387  std::ofstream OutFile("TrainData.csv");
15388 
15389  if(OutFile == 0)
15390  {
15391  ShowMessage("Output file TrainData.csv failed to open");
15392  Utilities->CallLogPop(1372);
15393  return;
15394  }
15395  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15396  {
15397  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15398  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
15399 
15400  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
15401  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
15402 
15403  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
15404  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
15405  "RepeatNumber" << '\n' << '\n';
15406  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15407  {
15408  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15409  AnsiString TimetableEntryTypeStr;
15410  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
15411  switch(AVEntry.FormatType)
15412  {
15413  case 0:
15414  {
15415  TimetableEntryTypeStr = "NoFormat";
15416  break;
15417  }
15418 
15419  case 1:
15420  {
15421  TimetableEntryTypeStr = "TimeLoc";
15422  break;
15423  }
15424 
15425  case 2:
15426  {
15427  TimetableEntryTypeStr = "TimeTimeLoc";
15428  break;
15429  }
15430 
15431  case 3:
15432  {
15433  TimetableEntryTypeStr = "TimeCmd";
15434  break;
15435  }
15436 
15437  case 4:
15438  {
15439  TimetableEntryTypeStr = "StartNew";
15440  break;
15441  }
15442 
15443  case 5:
15444  {
15445  TimetableEntryTypeStr = "TimeCmdHeadCode";
15446  break;
15447  }
15448 
15449  case 6:
15450  {
15451  TimetableEntryTypeStr = "FinRemHere";
15452  break;
15453  }
15454 
15455  case 7:
15456  {
15457  TimetableEntryTypeStr = "FNSShuttle";
15458  break;
15459  }
15460 
15461  case 8:
15462  {
15463  TimetableEntryTypeStr = "SNTShuttle";
15464  break;
15465  }
15466 
15467  case 9:
15468  {
15469  TimetableEntryTypeStr = "SNSShuttle";
15470  break;
15471  }
15472 
15473  case 10:
15474  {
15475  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
15476  break;
15477  }
15478 
15479  case 11:
15480  {
15481  TimetableEntryTypeStr = "FSHNewService";
15482  break;
15483  }
15484 
15485  case 12:
15486  {
15487  TimetableEntryTypeStr = "Repeat";
15488  break;
15489  }
15490 
15491  default:
15492  {
15493  TimetableEntryTypeStr = "Default";
15494  break;
15495  }
15496  }
15497  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
15498  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
15499  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
15500  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
15501  AVEntry.NumberOfRepeats << '\n';
15502  }
15503  OutFile << '\n';
15504  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
15505  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
15506  {
15507  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
15508  AnsiString RunningEntryStr;
15509  // NotStarted, Running, Exited
15510  switch(TOD.RunningEntry)
15511  {
15512  case 0:
15513  {
15514  RunningEntryStr = "NotStarted";
15515  break;
15516  }
15517 
15518  case 1:
15519  {
15520  RunningEntryStr = "Running";
15521  break;
15522  }
15523 
15524  case 2:
15525  {
15526  RunningEntryStr = "Exited";
15527  break;
15528  }
15529  }
15530  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
15531  }
15532  OutFile << '\n';
15533  }
15534  OutFile.close();
15535  Utilities->CallLogPop(1373);
15536 }
15537 
15538 // ---------------------------------------------------------------------------
15539 
15540 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
15541 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
15542 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
15543 {
15544  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
15545  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
15547  ShowMessage(Message);
15548  BaseTime = TDateTime::CurrentDateTime();
15549  StopTTClockFlag = false;
15550  Utilities->CallLogPop(1374);
15551 }
15552 
15553 // ---------------------------------------------------------------------------
15554 
15555 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
15556 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
15557 // from the start of the relevant vectors. Can't save the pointer values
15558 // as these will be different each time the vectors are created
15559 {
15560  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
15561  Utilities->SaveFileInt(SessionFile, TrainVector.size());
15562  for(unsigned int x = 0; x < TrainVector.size(); x++)
15563  {
15564  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
15565  }
15566  Utilities->CallLogPop(1375);
15567 }
15568 
15569 // ---------------------------------------------------------------------------
15570 
15571 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
15572 {
15573  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
15574  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
15575  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15576  // by zero error in calculating AValue, use 1
15577  for(int x = 0; x < NumberOfTrains; x++)
15578  {
15579  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15580  // by zero error in calculating AValue, use 1
15581  NewTrain->LoadOneSessionTrain(0, SessionFile);
15582  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
15583  // added at v2.4.0. have to include as that value not stored in session file
15584  {
15585  NewTrain->StoppedWithoutPower = true;
15586  }
15587  TrainVector.push_back(*NewTrain);
15588  LastTrainLoaded = x;
15589  }
15590  delete NewTrain;
15591  Utilities->CallLogPop(1376);
15592 }
15593 
15594 // ---------------------------------------------------------------------------
15595 
15596 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
15597 {
15598  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
15599  int NumberOfTrains;
15600 
15601  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
15602  {
15603  Utilities->CallLogPop(1377);
15604  return(false);
15605  }
15606  for(int x = 0; x < NumberOfTrains; x++)
15607  {
15608  if(!(TTrain::CheckOneSessionTrain(InFile)))
15609  {
15610  Utilities->CallLogPop(1378);
15611  return(false);
15612  }
15613  }
15614  Utilities->CallLogPop(1379);
15615  return(true);
15616 }
15617 
15618 // ---------------------------------------------------------------------------
15619 
15620 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
15621 {
15622  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
15623  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
15624  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
15625  {
15626  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
15627  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
15628  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
15629  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
15630  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
15631  }
15632  Utilities->CallLogPop(1380);
15633 }
15634 
15635 // ---------------------------------------------------------------------------
15636 
15637 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15638 {
15639  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
15640  TAllRoutes::TLockedRouteClass LockedRouteObject;
15641  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
15642 
15643  for(int x = 0; x < LockedRouteVectorSize; x++)
15644  {
15645  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15646  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15647  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15648  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
15649  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
15650  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
15651  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
15652  }
15653  Utilities->CallLogPop(1381);
15654 }
15655 
15656 // ---------------------------------------------------------------------------
15657 
15658 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15659 {
15660  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
15661  int LockedRouteVectorSize;
15662 
15663  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
15664  {
15665  Utilities->CallLogPop(1382);
15666  return(false);
15667  }
15668  for(int x = 0; x < LockedRouteVectorSize; x++)
15669  {
15670  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15671  {
15672  Utilities->CallLogPop(1383);
15673  return(false);
15674  }
15675  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15676  {
15677  Utilities->CallLogPop(1384);
15678  return(false);
15679  }
15680  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15681  {
15682  Utilities->CallLogPop(1385);
15683  return(false);
15684  }
15685  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15686  {
15687  Utilities->CallLogPop(1386);
15688  return(false);
15689  }
15690  if(!Utilities->CheckFileDouble(SessionFile))
15691  {
15692  Utilities->CallLogPop(1387);
15693  return(false);
15694  }
15695  }
15696  Utilities->CallLogPop(1388);
15697  return(true);
15698 }
15699 
15700 // ---------------------------------------------------------------------------
15701 
15702 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
15703 {
15704  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
15705  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
15706  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
15707  {
15708  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
15709  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
15710  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
15711  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
15712  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
15713  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
15714  }
15715  Utilities->CallLogPop(1389);
15716 }
15717 
15718 // ---------------------------------------------------------------------------
15719 
15720 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15721 {
15722  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
15723  TContinuationAutoSigEntry ContinuationAutoSigObject;
15724  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
15725 
15726  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15727  {
15728  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15729  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
15730  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
15731  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
15732  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
15733  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
15734  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
15735  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
15736  }
15737  Utilities->CallLogPop(1390);
15738 }
15739 
15740 // ---------------------------------------------------------------------------
15741 
15742 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15743 {
15744  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
15745  int ContinuationAutoSigVectorSize;
15746 
15747  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
15748  {
15749  Utilities->CallLogPop(1391);
15750  return(false);
15751  }
15752  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15753  {
15754  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15755  {
15756  Utilities->CallLogPop(1392);
15757  return(false);
15758  }
15759  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15760  {
15761  Utilities->CallLogPop(1393);
15762  return(false);
15763  }
15764  if(!Utilities->CheckFileDouble(SessionFile))
15765  {
15766  Utilities->CallLogPop(1405);
15767  return(false);
15768  }
15769  if(!Utilities->CheckFileDouble(SessionFile))
15770  {
15771  Utilities->CallLogPop(1406);
15772  return(false);
15773  }
15774  if(!Utilities->CheckFileDouble(SessionFile))
15775  {
15776  Utilities->CallLogPop(1407);
15777  return(false);
15778  }
15779  if(!Utilities->CheckFileDouble(SessionFile))
15780  {
15781  Utilities->CallLogPop(1394);
15782  return(false);
15783  }
15784  }
15785  Utilities->CallLogPop(1395);
15786  return(true);
15787 }
15788 
15789 // ---------------------------------------------------------------------------
15790 
15791 /*
15792  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
15793  {
15794  public:
15795  AnsiString Description; ///< service description
15796  AnsiString HeadCode; ///< service headcode
15797  int RepeatNumber; ///< service RepeatNumber
15798  int IncrementalMinutes; ///< Repeat separation in minutes
15799  int IncrementalDigits; ///< Repeat headcode separation
15800  int VectorPosition; ///< TrackVectorPosition for the continuation element
15801  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
15802  };
15803 
15804 
15805  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
15806  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
15807 */
15808 
15810 // build this into timetable load so session loading can use it too
15811 // being a multimap it automatically sorts in ascending EventTime order
15812 {
15813  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
15815  // need to clear as this called twice when load a session
15816  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15817  {
15818  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
15819  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
15820  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
15821 
15822  if(AVFirstEntry.Command == "Snt")
15823  // new train (no need to include Snt-sh since they can't start at a continuation)
15824  {
15827  {
15829  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
15830  // retains this value for all repeats
15831  CTEEntry.RepeatNumber = 0; // for first entry
15832  CTEEntry.TrainDataEntryPtr = &TDEntry;
15833  // retains this value for all repeats
15834  CTEEntry.HeadCode = TDEntry.HeadCode;
15835  CTEEntry.Description = TDEntry.Description;
15836  CTEEntry.IncrementalMinutes = 0;
15837  CTEEntry.IncrementalDigits = 0;
15838  if(AVLastEntry.FormatType == Repeat)
15839  {
15840  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
15841  // retains this value or 0 for all repeats
15842  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
15843  // retains this value or 0 for all repeats
15844  }
15845  CTEMMP.first = AVFirstEntry.EventTime;
15846  CTEMMP.second = CTEEntry;
15847  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15848  // base entry
15849  if(TDEntry.NumberOfTrains > 1)
15850  {
15851  if(AVLastEntry.FormatType != Repeat)
15852  {
15853  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
15854  }
15855  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
15856  {
15857  CTEEntry.RepeatNumber = y;
15858  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
15859  // CTEEntry.VectorPosition stays same
15860  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
15861  CTEMMP.second = CTEEntry;
15862  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15863  }
15864  }
15865  }
15866  }
15867  }
15868  Utilities->CallLogPop(1396);
15869 }
15870 
15871 // ---------------------------------------------------------------------------
15872 
15874 {
15875  // called when WarningFlashCount == 0 or when press zoomout button
15876  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
15877  if(!Display->ZoomOutFlag)
15878  {
15879  Utilities->CallLogPop(1156);
15880  return;
15881  }
15882  for(unsigned int x = 0; x < TrainVector.size(); x++)
15883  {
15884  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
15885  // if OldPlotElement[x] == -1 then ignore (not plotted)
15887  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
15888  }
15889  Display->Update();
15890  // need to keep this since Update() not called for PlotSmallOutput as too slow
15891  Utilities->CallLogPop(742);
15892 }
15893 
15894 // ---------------------------------------------------------------------------
15895 
15896 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
15897 {
15898  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
15899  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
15900  {
15901  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
15902  }
15903  Utilities->CallLogPop(740);
15904  return(TrainVector.at(VecPos));
15905 }
15906 
15907 // ---------------------------------------------------------------------------
15908 
15909 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
15910 {
15911  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
15912  AnsiString RetStr = "", PartStr = "";
15913 
15914 
15915 /*
15916  Have description & mass etc for train at top - header, then array of actions
15917 
15918  class TActionVectorEntry
15919  {
15920  public:
15921  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
15923  bool SignallerControl;
15925  bool Warning;
15927  int NumberOfRepeats;
15929  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15931  TDateTime EventTime, ArrivalTime, DepartureTime;
15933  TNumList ExitList;
15935  TTimetableFormatType FormatType;
15937  TTimetableLocationType LocationType;
15939  TTimetableSequenceType SequenceType;
15941  TTimetableShuttleLinkType ShuttleLinkType;
15943  TTrainDataEntry *LinkedTrainEntryPtr;
15945  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
15947 
15948  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
15949 
15950  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
15951 
15952  class TTrainOperatingData
15953  {
15954  public:
15955  int TrainID;
15956  TActionEventType EventReported;
15957  TRunningEntry RunningEntry;
15958 
15959  //inline function
15960  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
15961  };
15962 
15963  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
15964 
15965  class TTrainDataEntry
15966  {
15967  public:
15968  AnsiString HeadCode, ServiceReference, Description;
15970  double MaxBrakeRate;
15972  double MaxRunningSpeed;
15974  double PowerAtRail;
15976  int Mass;
15978  int NumberOfTrains;
15980  int SignallerSpeed;
15982  int StartSpeed;
15984  TActionVector ActionVector;
15986  TTrainOperatingDataVector TrainOperatingDataVector;
15988 
15989  //inline function
15990  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
15991  };
15992 
15993  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
15994 
15995  //formatted timetable types
15996  class TOneTrainFormattedEntry
15997  {
15998  AnsiString Action;//includes location if relevanr
15999  AnsiString Time;
16000  };
16001 
16002  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
16003 
16004  class TOneCompleteFormattedTrain//headcode + list of actions
16005  {
16006  public:
16007  AnsiString HeadCode;
16008  TOneFormattedTrainVector OneFormattedTrainVector;
16009  };
16010 
16011  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
16012 
16013  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
16014  {
16015  public:
16016  AnsiString Header;//description, mass, power, brake rate etc
16017  int NumberOfTrains;// number of repeats + 1
16018  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
16019  };
16020 
16021 
16022  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
16023  //end of formatted timetable types
16024 
16025 */
16026 
16027  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16028 
16029  // format "16/06/2009 20:55:17"
16030  // avoid characters in filename:= / \ : * ? " < > |
16031  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16032 
16033  AnsiString ShortTTName = "";
16034 
16035  for(int x = TTFileName.Length(); x > 0; x--)
16036  {
16037  if(TTFileName[x] == '\\')
16038  {
16039  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
16040  break;
16041  }
16042  }
16043 
16044  ShowMessage("Creates two timetables named " + ShortTTName +
16045  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
16046 
16047  Screen->Cursor = TCursor(-11); // Hourglass
16048 
16049  AnsiString FormatNoDPStr = "#######0";
16050  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
16051 
16053  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
16054  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
16055 
16056  // all timetable in formatted form
16057  //create the AllTTTrains vector
16058  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16059  {
16060  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
16061  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
16062  if(TrainDataEntry.Mass > 0)
16063  {
16064  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
16065  }
16066  if(TrainDataEntry.PowerAtRail > 0)
16067  {
16068  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
16069  }
16070  if(TrainDataEntry.MaxBrakeRate > 0)
16071  {
16072  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
16073  }
16074  if(TrainDataEntry.MaxRunningSpeed > 0)
16075  {
16076  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
16077  }
16078  FirstHeadCode = TrainDataEntry.HeadCode;
16079  int IncDigits = 0, IncMinutes = 0;
16080  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16081  if(!ActionVector.empty())
16082  {
16083  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16084  {
16085  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
16086  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16087  }
16088  }
16089  TTrainFormattedInformation OneTTLine;
16090  // contains all information for a single TT entry (including repeats)
16091  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
16092  {
16093  OneTTLine.Header = "";
16094  if((TrainDataEntry.Description != "") && (MassStr != ""))
16095  {
16096  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
16097  }
16098  else if(TrainDataEntry.Description != "")
16099  {
16100  OneTTLine.Header = TrainDataEntry.Description;
16101  }
16102  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
16103  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
16104  for(unsigned int z = 0; z < ActionVector.size(); z++)
16105  {
16106  TOneTrainFormattedEntry OneTTEntry;
16107  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
16108  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
16109  AnsiString PartStr = "", TimeStr = "";
16110 /*
16111  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
16112  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
16113  ExitRailway};
16114  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
16115  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
16116  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
16117 */
16118  if(ActionVectorEntry.SequenceType == Start)
16119  {
16120  if(ActionVectorEntry.FormatType == StartNew)
16121  {
16122  if(ActionVectorEntry.LocationName != "")
16123  {
16124  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
16125  {
16126  PartStr = "Enters at " + ActionVectorEntry.LocationName;
16127  }
16128  else
16129  {
16130  PartStr = "Created at " + ActionVectorEntry.LocationName;
16131  }
16132  }
16133  else // may be a named continuation or other element, and if so report that
16134  {
16135  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
16136  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
16137  {
16138  if(LocName != "")
16139  {
16140  PartStr = "Enters at " + LocName;
16141  }
16142  else // use rear position if it's a continuation
16143  {
16144  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
16145  }
16146  }
16147  else // not a continuation
16148  {
16149  if(LocName != "")
16150  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
16151  // but include anyway
16152  {
16153  PartStr = "Created at " + LocName;
16154  }
16155  else // use rear position again
16156  {
16157  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
16158  }
16159  }
16160  }
16161  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
16162  }
16163  else if(ActionVectorEntry.FormatType == SNTShuttle)
16164  {
16165  if(y == 0) // first train
16166  {
16167  PartStr = "Enters at " + ActionVectorEntry.LocationName;
16168  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
16169  }
16170  else
16171  {
16172  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
16173  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
16174  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
16175  } // y-1 for headcode above since it is the last repeat value that the train is from
16176 
16177  }
16178  else if(ActionVectorEntry.Command == "Sfs")
16179  {
16180  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
16181  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16182  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
16183  }
16184  else if(ActionVectorEntry.Command == "Sns")
16185  {
16186  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16187  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16188  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
16189  }
16190  else if(ActionVectorEntry.FormatType == SNSShuttle)
16191  {
16192  if(y == 0) // first entry from shuttle
16193  {
16194  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16195  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
16196  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
16197  }
16198  else
16199  {
16200  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
16201  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
16202  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
16203  } // y-1 for headcode above since it is the last repeat value that the train is from
16204 
16205  }
16206  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
16207  {
16208  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16209  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
16210  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
16211  AnsiString FirstHeadCode = TDE->HeadCode;
16212  int LastRepeatNumber = TDE->NumberOfTrains - 1;
16213  // a shuttle has to have at least 1 repeat
16214  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
16215  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
16216  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
16217  }
16218  }
16219  else if(ActionVectorEntry.SequenceType == Intermediate)
16220  {
16221  if(ActionVectorEntry.FormatType == TimeTimeLoc)
16222  {
16223  // here need 2 entries if times different so push the first right away & the second later
16224  // if times same just give the arrival entry
16225  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
16226  {
16227  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
16228  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16229  OneTTEntry.Action = PartStr;
16230  OneTTEntry.Time = TimeStr;
16231  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
16232  PartStr = "Departs from " + ActionVectorEntry.LocationName;
16233  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
16234  }
16235  else
16236  {
16237  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
16238  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16239  }
16240  }
16241  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
16242  {
16243  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
16244  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16245  }
16246  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
16247  {
16248  PartStr = "Departs from " + ActionVectorEntry.LocationName;
16249  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
16250  }
16251  else if(ActionVectorEntry.FormatType == PassTime)
16252  {
16253  PartStr = "Passes " + ActionVectorEntry.LocationName;
16254  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
16255  }
16256  else if(ActionVectorEntry.Command == "jbo")
16257  {
16258  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
16259  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16260  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
16261  }
16262  else if(ActionVectorEntry.Command == "fsp")
16263  {
16264  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
16265  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16266  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
16267  }
16268  else if(ActionVectorEntry.Command == "rsp")
16269  {
16270  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
16271  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16272  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
16273  }
16274  else if(ActionVectorEntry.Command == "cdt")
16275  {
16276  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
16277  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
16278  }
16279  }
16280  else if(ActionVectorEntry.SequenceType == Finish)
16281  {
16282  if(ActionVectorEntry.Command == "Fns")
16283  {
16284  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16285  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16286  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
16287  }
16288  else if(ActionVectorEntry.Command == "F-nshs")
16289  {
16290  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16291  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
16292  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
16293  }
16294  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
16295  {
16296  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
16297  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
16298  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
16299  // y+1 because it's the NEXT service repeat number that is relevant
16300  }
16301  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
16302  {
16303  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16304  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
16305  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
16306  }
16307  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
16308  {
16309  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16310  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
16311  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
16312  // y+1 because it's the NEXT service repeat number that is relevant
16313  }
16314  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
16315  {
16316  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
16317  // only used in chronological tt
16318  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
16319  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
16320  }
16321  else if(ActionVectorEntry.Command == "Frh")
16322  {
16323  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
16324  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
16325  if(z > 0)
16326  // should be for finish entry but include check for safety
16327  {
16328  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16329  {
16330  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16331  }
16332  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16333  {
16334  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16335  }
16336  else
16337  {
16338  TimeStr = " "; // shouldn't ever get here
16339  }
16340  }
16341  }
16342  else if(ActionVectorEntry.Command == "Fer")
16343  {
16344  AnsiString AllowedExits;
16345  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
16346  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
16347  }
16348  else if(ActionVectorEntry.Command == "Fjo")
16349  {
16350  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
16351  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16352  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
16353  }
16354  }
16355  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
16356  {
16357  continue; // no entry needed for a repeat
16358  }
16359  OneTTEntry.Action = PartStr;
16360  OneTTEntry.Time = TimeStr;
16361  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
16362  // one per action
16363  }
16364  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
16365  // one per repeat
16366  }
16367  AllTTTrains->push_back(OneTTLine); // one per repeating train
16368  }
16369  // AllTTTrains vector now complete
16370 
16371  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
16372 
16373  if(TTFile == 0)
16374  {
16375  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
16376  delete AllTTTrains;
16377  Utilities->CallLogPop(1567);
16378  return;
16379  }
16380 /* formatted timetable types
16381  class TOneTrainFormattedEntry
16382  {
16383  AnsiString Action;//includes location if relevant
16384  AnsiString Time;
16385  };
16386 
16387  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
16388 
16389  class TOneCompleteFormattedTrain//headcode + list of actions
16390  {
16391  public:
16392  AnsiString HeadCode;
16393  TOneFormattedTrainVector OneFormattedTrainVector;
16394  };
16395 
16396  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
16397 
16398  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
16399  {
16400  public:
16401  AnsiString Header;//description, mass, power, brake rate etc
16402  int NumberOfTrains;// number of repeats + 1
16403  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
16404  };
16405 
16406  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
16407  //end of formatted timetable types
16408 */
16409 
16410  // new layout using multiple rows
16411  TTFile << TableTitle.c_str() << '\n' << '\n';
16412  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16413  {
16414  TTFile << AllTTTrains->at(x).Header.c_str();
16415  TTFile << '\n';
16416  TTFile << ','; // for the blank line above the action list
16417  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16418  {
16419  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16420  {
16421  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
16422  }
16423  else
16424  {
16425  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
16426  }
16427  }
16428  TTFile << '\n' << '\n';
16429 
16430  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
16431  {
16432  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
16433  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16434  {
16435  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16436  {
16437  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
16438  }
16439  else
16440  {
16441  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
16442  }
16443  }
16444  TTFile << '\n';
16445  }
16446  TTFile << '\n' << '\n';
16447  }
16448 
16449  TTFile.close();
16450 
16451  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16452 
16453  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16454 
16455  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
16456 
16457  if(TTFile2 == 0)
16458  {
16459  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
16460  delete AllTTTrains;
16461  Utilities->CallLogPop(1710);
16462  return;
16463  }
16464  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
16465  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
16466  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
16467 
16468  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
16469  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
16470 
16471  // multimap of AnsiStrings with TimeString as key (to sort automatically)
16472 
16473  TTFile2 << TableTitle.c_str() << '\n' << '\n';
16474  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16475  {
16476  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16477  {
16478  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
16479  {
16480  bool GiveMessagesFalse = false;
16481  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
16482  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
16483  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
16484  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
16485  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
16486  {
16487  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
16488  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
16489  TimeString = TimeString.SubString(9, 5);
16490  ActionString += " " + OtherHeadCode;
16491  }
16492  if(TimeString.SubString(1, 7) == "End at ")
16493  // for Frh-sh final entry
16494  {
16495  TimeString = TimeString.SubString(8, 5);
16496  }
16497  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
16498  AnsiMultiMapEntry.first = TimeString;
16499  AnsiMultiMapEntry.second = OneLine;
16500  TAMM->insert(AnsiMultiMapEntry);
16501  }
16502  }
16503  }
16504 
16505  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
16506  {
16507  TTFile2 << (AMMIT->second).c_str();
16508  }
16509  delete AllTTTrains;
16510  delete TAMM;
16511  TTFile2.close();
16512  Utilities->CallLogPop(1580);
16513 }
16514 
16515 // ---------------------------------------------------------------------------
16516 
16517 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
16518  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
16519 {
16520 
16521  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
16522  bool AnalysisError = false;
16523  AnsiString SequenceLog = "SequenceLog\n";
16524 
16525 /* Double crosslink (shuttle) table:
16526 
16527 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
16528  Code ShuttleLink- EntryPtr ShuttleLink-
16529  HeadCode EntryPtr
16530 
16531 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
16532 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
16533 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
16534 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
16535 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
16536 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
16537 
16538 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
16539 */
16540 
16541  try
16542  {
16543  //New section at v2.5.0 for tt conflict analysis
16544  /*
16545  typedef std::list<AnsiString> TServiceCallingLocsList;
16546  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
16547 
16549  struct TLocServiceTimes
16550  {
16551  AnsiString Location;
16552  AnsiString ServiceAndRepeatNum;
16553  AnsiString AtLocTime;
16554  AnsiString ArrTime;
16555  AnsiString DepTime;
16556  AnsiString FrhMarker;
16557  };
16558  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16559  */
16560 
16561  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
16562  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
16563 
16564  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
16565  TTrainDataVector::iterator TDVIt, TDVCopyIt;
16566  int Suffix = 0;
16567  int IteratorNumber = 0;
16568  AnsiString AnsiSuffix = "";
16569  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
16570  {
16571  IteratorNumber++; //first value in loop is 1
16572  Suffix = 0;
16573  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
16574  {
16575  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
16576  {
16577  Suffix++; //first value is 1
16578  AnsiSuffix = AnsiString(Suffix);
16579  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
16580  }
16581  }
16582  }
16583  SequenceLog += "1\n";
16584  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
16585  TServiceCallingLocsList ServiceCallingLocsList;
16586  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
16587  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16588  {
16589  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16590  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16591  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
16592  ServiceCallingLocsList.clear();
16593  if(ActionVector.empty())
16594  {
16595  continue;
16596  }
16597  if(ActionVector.at(0).SignallerControl)
16598  {
16599  continue;
16600  }
16601  for(unsigned int z = 0; z < ActionVector.size(); z++)
16602  {
16603  TActionVectorEntry AVE = ActionVector.at(z);
16604  if(AVE.FormatType == StartNew)
16605  {
16606  if(AVE.LocationType == AtLocation) //located Snt
16607  {
16608  ServiceCallingLocsList.push_back(AVE.LocationName);
16609  }
16610  else //unlocated Snt (could be entering at continuation)
16611  {
16613  if(TE.ActiveTrackElementName != "")
16614  {
16615  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
16616  }
16617  else
16618  {
16619  int HLoc = TE.HLoc;
16620  int VLoc = TE.VLoc;
16621  AnsiString HString;
16622  AnsiString VString;
16623  if(HLoc < 0)
16624  {
16625  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16626  }
16627  else
16628  {
16629  HString = AnsiString(HLoc);
16630  }
16631  if(VLoc < 0)
16632  {
16633  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16634  }
16635  else
16636  {
16637  VString = AnsiString(VLoc);
16638  }
16639  ServiceCallingLocsList.push_back(HString + '-' + VString);
16640  }
16641  }
16642  }
16643  else if(AVE.SequenceType == Start) //other start entries, all located
16644  {
16645  ServiceCallingLocsList.push_back(AVE.LocationName);
16646  }
16647  else if(AVE.FormatType == TimeLoc) //z must be > 0
16648  {
16649  if(ServiceCallingLocsList.back() != AVE.LocationName)
16650  {
16651  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
16652  }
16653  }
16654  else if(AVE.FormatType == PassTime)
16655  {
16656  ServiceCallingLocsList.push_back(AVE.LocationName);
16657  }
16658  else if(AVE.FormatType == TimeTimeLoc)
16659  {
16660  ServiceCallingLocsList.push_back(AVE.LocationName);
16661  }
16662  else if(AVE.Command == "cdt") //list if not next to start or finish
16663  {
16664  if(ActionVector.at(z-1).SequenceType == Start)
16665  {
16666  continue;
16667  }
16668  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
16669  {
16670  continue;
16671  }
16672  else
16673  {
16674  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
16675  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
16676  }
16677  }
16678  else if(AVE.FormatType == ExitRailway) //Fer
16679  {
16680  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
16681  AnsiString LName = TE.ActiveTrackElementName;
16682  if(LName != "")
16683  {
16684  ServiceCallingLocsList.push_back(LName);
16685  }
16686  else
16687  {
16688  int HLoc = TE.HLoc;
16689  int VLoc = TE.VLoc;
16690  AnsiString HString;
16691  AnsiString VString;
16692  if(HLoc < 0)
16693  {
16694  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16695  }
16696  else
16697  {
16698  HString = AnsiString(HLoc);
16699  }
16700  if(VLoc < 0)
16701  {
16702  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16703  }
16704  else
16705  {
16706  VString = AnsiString(VLoc);
16707  }
16708  ServiceCallingLocsList.push_back(HString + '-' + VString);
16709  }
16710  }
16711  }
16712  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
16713  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
16714  }
16715  //AllServiceCallingLocsMap built
16716  SequenceLog += "2\n";
16717  //test validity of AllServiceCallingLocsMap
16718 /*
16719  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16720  std::ofstream Test(TestFile.c_str());
16721 
16722  if(TestFile == 0)
16723  {
16724  ShowMessage("TestFile failed to open - can't be created");
16725  Utilities->CallLogPop();
16726  return false;
16727  }
16728 
16729  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
16730  {
16731  Test << ASCLIt->first << '\n'; //service ref
16732  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
16733  {
16734  Test << *SCLIt << '\n';
16735  }
16736  Test << "\n\n";
16737  }
16738  Test.close();
16739  Utilities->CallLogPop();
16740  return true;
16741 */
16742 
16743  //initialise variables before calc LastTTTime & build LocServiceTimesVector
16744  if(TrainDataVector.empty())
16745  {
16746  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
16747  Utilities->CallLogPop(2209);
16748  return(false);
16749  }
16750  TLocServiceTimes TLSTEntry;
16751  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
16752  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
16753  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
16754  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
16755  LastTTTime = "";
16756  SequenceLog += "3\n";
16757  //calculate LastTTTime
16758  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16759  {
16760  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16761  TActionVector &ActionVector = TrainDataEntry.ActionVector;
16762  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
16763  TDateTime LastTDTime;
16764  int IncMinutes = 0;
16765  NumTrains = TrainDataEntry.NumberOfTrains;
16766  if(ActionVector.empty())
16767  {
16768  continue;
16769  }
16770  if(ActionVector.at(0).SignallerControl)
16771  {
16772  continue;
16773  }
16774  if(AVLast->FormatType == Repeat)
16775  {
16776  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16777  AVLast--; //now points to the command before the repeat
16778  }
16779  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
16780  {
16781  AVLast--; //points to last timed entry
16782  }
16783  //here AVLast points to last entry with a time
16784  if(AVLast->ArrivalTime != TDateTime(-1))
16785  {
16786  LastTDTime = AVLast->ArrivalTime;
16787  }
16788  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
16789  {
16790  LastTDTime = AVLast->EventTime;
16791  }
16792  else
16793  {
16794  continue; //shouldn't ever reach here but if do then skip this service
16795  }
16796  if(NumTrains == 1)
16797  {
16798  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
16799  }
16800  else
16801  {
16802  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
16803  }
16804  if(LastAnsiTime > LastTTTime)
16805  {
16806  LastTTTime = LastAnsiTime;
16807  }
16808  }
16809  SequenceLog += "4\n";
16810 //build LocServiceTimesVector
16811 
16812 /*
16813  struct TLocServiceTimes
16814  {
16815  AnsiString Location;
16816  AnsiString ServiceAndRepeatNum;
16817  AnsiString AtLocTime;
16818  AnsiString ArrTime;
16819  AnsiString DepTime;
16820  AnsiString FrhMarker;
16821  };
16822  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16823 
16824 This works as follows:
16825 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
16826 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
16827 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
16828 
16829 Every action for every train is examined and times entered as follows:-
16830 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
16831 b) an unlocated Snt: entry time becomes DepTime
16832 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
16833 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
16834 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
16835 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
16836 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
16837 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
16838 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
16839 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
16840 */
16841  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16842  {
16843  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16844  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16845  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
16846  int IncMinutes = 0;
16847  NumTrains = TrainDataEntry.NumberOfTrains;
16848  if(ActionVector.empty())
16849  {
16850  continue;
16851  }
16852  if(ActionVector.at(0).SignallerControl)
16853  {
16854  continue;
16855  }
16856  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16857  {
16858  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16859  }
16860  for(int y = 0; y < NumTrains; y++) //y is the repeat number
16861  {
16862  if(NumTrains == 1)
16863  {
16864  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
16865  }
16866  else if(y == 0)
16867  {
16868  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
16869  }
16870  else
16871  {
16872  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
16873  }
16874  for(unsigned int z = 0; z < ActionVector.size(); z++)
16875  {
16876  TActionVectorEntry AVE = ActionVector.at(z);
16877  TLSTEntry.AtLocTime = "";
16878  TLSTEntry.ArrTime = "";
16879  TLSTEntry.DepTime = "";
16880  TLSTEntry.Location = "";
16881  TLSTEntry.FrhMarker = "";
16882 
16883  if(AVE.FormatType == StartNew) //Snt only
16884  {
16885  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
16886  {
16887  TLSTEntry.Location = AVE.LocationName;
16888  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
16889  LocServiceTimesVector.push_back(TLSTEntry);
16890 
16891  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16892  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
16893  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16894  {
16895  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16896  {
16897  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
16898  break;
16899  }
16900  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16901  {
16902  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
16903  break;
16904  }
16905  }
16906  if(FoundStopTime == "")
16907  {
16908  throw Exception("Failure to determine FoundStopTime for located Snt");
16909  }
16910  int WhileCount = 0;
16911  while(true)
16912  {
16913  //add minutes until reach FoundStopTime but don't add that time
16914  WhileCount++;
16915  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16916  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16917  TLSTEntry.DepTime = "";
16918  TLSTEntry.ArrTime = "";
16919  if(IncTime >= FoundStopTime) //don't add that time
16920  {
16921  break;
16922  }
16923  LocServiceTimesVector.push_back(TLSTEntry);
16924  if(WhileCount > 2000)
16925  {
16926  throw Exception("While loop failed to break in 2000 loops for located Snt");
16927  }
16928  }
16929  }
16930  else //unlocated Snt, use the EventTime as DepTime for this vector
16931  {
16933  if(TE.ActiveTrackElementName != "")
16934  {
16935  TLSTEntry.Location = TE.ActiveTrackElementName;
16936  }
16937  else
16938  {
16939  int HLoc = TE.HLoc;
16940  int VLoc = TE.VLoc;
16941  AnsiString HString;
16942  AnsiString VString;
16943  if(HLoc < 0)
16944  {
16945  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16946  }
16947  else
16948  {
16949  HString = AnsiString(HLoc);
16950  }
16951  if(VLoc < 0)
16952  {
16953  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16954  }
16955  else
16956  {
16957  VString = AnsiString(VLoc);
16958  }
16959  TLSTEntry.Location = HString + '-' + VString;
16960  }
16961  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
16962  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16963  LocServiceTimesVector.push_back(TLSTEntry);
16964  }
16965  }
16966 
16967  else if(AVE.SequenceType == Start) //other start entries, all located
16968  {
16969  TLSTEntry.Location = AVE.LocationName;
16970  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
16971  LocServiceTimesVector.push_back(TLSTEntry);
16972  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16973  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16974  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16975  {
16976  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16977  {
16978  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
16979  break;
16980  }
16981  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16982  {
16983  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
16984  break;
16985  }
16986  }
16987  if(FoundStopTime == "")
16988  {
16989  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16990  }
16991  int WhileCount = 0;
16992  while(true)
16993  {
16994  //add minutes until reach FoundStopTime but don't add that time
16995  WhileCount++;
16996  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16997  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16998  TLSTEntry.DepTime = "";
16999  TLSTEntry.ArrTime = "";
17000  if(IncTime >= FoundStopTime) //don't add that time
17001  {
17002  break;
17003  }
17004  LocServiceTimesVector.push_back(TLSTEntry);
17005  if(WhileCount > 2000)
17006  {
17007  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
17008  }
17009  }
17010  }
17011 
17012  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
17013  {
17014  TLSTEntry.Location = AVE.LocationName;
17015  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
17016  {
17017  bool SkipAddingMinutes = false;
17018  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
17019  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
17020  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
17021  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
17022  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
17023  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
17024  {
17025  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
17026  {
17027  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
17028  break;
17029  }
17030  if(ActionVector.at(a).SequenceType == Finish) //finish catered for in a later test
17031  {
17032  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
17033  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
17034  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
17035  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
17036  {
17037  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
17038  SkipAddingMinutes = true;
17039  }
17040  break;
17041  }
17042  }
17043  if(FoundStopTime == "")
17044  {
17045  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
17046  }
17047  if(!SkipAddingMinutes)
17048  {
17049  int WhileCount = 0;
17050  while(true)
17051  {
17052  //add minutes until reach FoundStopTime but don't add that time
17053  WhileCount++;
17054  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17055  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
17056  TLSTEntry.DepTime = "";
17057  TLSTEntry.ArrTime = "";
17058  if(IncTime >= FoundStopTime) //don't add that time
17059  {
17060  break;
17061  }
17062  LocServiceTimesVector.push_back(TLSTEntry);
17063  if(WhileCount > 2000)
17064  {
17065  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
17066  }
17067  }
17068  }
17069  }
17070  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
17071  {
17072  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
17073  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
17074  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
17075  {
17076  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
17077  {
17078  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
17079  LocServiceTimesVector.pop_back();
17080  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
17081  }
17082  else //just add the dep & atloc times
17083  {
17084  TLSTEntry.ArrTime = "";
17085  LocServiceTimesVector.push_back(TLSTEntry);
17086  }
17087  }
17088  else //just add the dep & atloc times
17089  {
17090  TLSTEntry.ArrTime = "";
17091  LocServiceTimesVector.push_back(TLSTEntry);
17092  }
17093  }
17094  }
17095 
17096  else if(AVE.FormatType == TimeTimeLoc)
17097  {
17098  TLSTEntry.Location = AVE.LocationName;
17099  if(AVE.ArrivalTime > TDateTime(-1)) //should be
17100  {
17101  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
17102  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
17103  }
17104  if(AVE.DepartureTime > TDateTime(-1)) //should be
17105  {
17106  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
17107  }
17108  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
17109  {
17110  LocServiceTimesVector.push_back(TLSTEntry);
17111  }
17112  else
17113  {
17114  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
17115  TLSTEntry.DepTime = "";
17116  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
17117  TLSTEntry.ArrTime = ""; //done with this now
17118  while(TLSTEntry.AtLocTime < TempDepTime)
17119  {
17120  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17121  if(TLSTEntry.AtLocTime == TempDepTime)
17122  {
17123  TLSTEntry.DepTime = TempDepTime; //restore value
17124  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
17125  }
17126  else
17127  {
17128  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
17129  }
17130  }
17131  }
17132  }
17133 
17134  else if(AVE.FormatType == PassTime) //added at v2.9.1
17135  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
17136  TLSTEntry.Location = AVE.LocationName;;
17137  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
17138  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
17139  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
17140  TLSTEntry.ArrTime = ""; //need to reset this to null
17141  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
17142  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
17143  }
17144 
17145  else if(AVE.FormatType == ExitRailway) //Fer
17146  {
17147  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
17148  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
17149  //be wrong, but can't guess from here & most will have same name
17150  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
17151  if(LName != "")
17152  {
17153  TLSTEntry.Location = LName;
17154  }
17155  else
17156  {
17157  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
17158  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
17159  AnsiString HString;
17160  AnsiString VString;
17161  if(HLoc < 0)
17162  {
17163  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17164  }
17165  else
17166  {
17167  HString = AnsiString(HLoc);
17168  }
17169  if(VLoc < 0)
17170  {
17171  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17172  }
17173  else
17174  {
17175  VString = AnsiString(VLoc);
17176  }
17177  TLSTEntry.Location = HString + '-' + VString;
17178  }
17179  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
17180  }
17181 
17182  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
17183  {
17184  AnsiString FrhTime;
17185  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
17186  {
17187  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
17188  }
17189  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
17190  {
17191  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
17192  }
17193  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
17194  TLSTEntry.Location = AVE.LocationName;
17195  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17196  TLSTEntry.FrhMarker = "Frh";
17197  LocServiceTimesVector.push_back(TLSTEntry);
17198  TLSTEntry.FrhMarker = "";
17199  //add all times from next minute to end of timetable
17200  while(IncTime <= LastTTTime)
17201  {
17202  TLSTEntry.AtLocTime = IncTime;
17203  LocServiceTimesVector.push_back(TLSTEntry);
17204  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
17205  }
17206  }
17207 
17208  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
17209  {
17210  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
17211  {
17212  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
17213  TLSTEntry.Location = AVE.LocationName;
17214  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17215  TLSTEntry.FrhMarker = "Frh";
17216  LocServiceTimesVector.push_back(TLSTEntry);
17217  TLSTEntry.FrhMarker = "";
17218  //add all times from next minute to end of timetable
17219  while(IncTime <= LastTTTime)
17220  {
17221  TLSTEntry.AtLocTime = IncTime;
17222  LocServiceTimesVector.push_back(TLSTEntry);
17223  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
17224  }
17225  }
17226  }
17227 
17228  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
17229  {
17230  //nothing is done here as the entry will be listed at this time under the new service reference
17231  }
17232  }
17233  }
17234  }
17235  SequenceLog += "5\n";
17236  //now sort in location order
17237  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
17238  //LocServiceTimesVector now complete & sorted in location order
17239 
17241 /*
17242  std::ofstream LSTVFile("LSTVFile.txt");
17243  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
17244  {
17245  LSTVFile << LSTVIt->Location + '\n';
17246  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
17247  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
17248  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
17249  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
17250  if(LSTVIt->FrhMarker == "")
17251  {
17252  LSTVFile << "Not Frh\n";
17253  }
17254  else
17255  {
17256  LSTVFile << LSTVIt->FrhMarker + '\n';
17257  }
17258  LSTVFile << '\n';
17259  }
17260  LSTVFile.close();
17261 */
17262  //declare pointers for use in printouts
17263  TLocServiceTimesVector::iterator Ptr1, Ptr2;
17264 
17265  //set up the output file
17266  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17267  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
17268 
17269  std::ofstream TTFile3(TTFileName3.c_str());
17270 
17271  if(TTFile3 == 0)
17272  {
17273  ShowMessage("Conflict Analysis file failed to open - can't be created");
17274  Utilities->CallLogPop(2210);
17275  return(false);
17276  }
17277  if(LocServiceTimesVector.empty())
17278  {
17279  ShowMessage("No timetabled services found");
17280  TTFile3.close();
17281  DeleteFile(TTFileName3);
17282  Utilities->CallLogPop(2211);
17283  return(false);
17284  }
17285  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
17286  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
17287  SequenceLog += "6\n";
17288 
17289 /* print out TrainDataVectorCopy for debugging purposes
17290 std::ofstream TDVCFile("TDVCFile.txt");
17291 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
17292 {
17293  TDVCFile << TDVCIt->ServiceReference + '\n';
17294  TDVCFile << TDVCIt->Description + '\n';
17295  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
17296  {
17297  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
17298  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
17299  {
17300  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
17301  }
17302  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
17303  {
17304  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
17305  }
17306  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
17307  {
17308  TDVCFile << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
17309  }
17310  else if(AVE.FormatType == TimeTimeLoc)
17311  {
17312  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
17313  }
17314  else if(AVE.FormatType == PassTime)
17315  {
17316  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
17317  }
17318  else if(AVE.FormatType == ExitRailway)
17319  {
17320  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << " Fer" << '\n';
17321  }
17322  else if(AVE.FormatType == FinRemHere)
17323  {
17324  TDVCFile << "Frh" << '\n';
17325  }
17326  }
17327  TDVCFile << '\n';
17328 }
17329 TDVCFile.close();
17330 
17331 */
17332  //arrivals
17333  if(ArrChecked)
17334  {
17335  //sort in ArrTime order for each location
17336  Ptr1 = LocServiceTimesVector.begin();
17337  Ptr2 = Ptr1 + 1;
17338  while(Ptr2 != LocServiceTimesVector.end())
17339  {
17340  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17341  {
17342  Ptr2++;
17343  if(Ptr2 == LocServiceTimesVector.end())
17344  {
17345  break;
17346  }
17347  }
17348  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
17349  Ptr1 = Ptr2; //first entry with next name
17350  if(Ptr2 != LocServiceTimesVector.end())
17351  {
17352  Ptr2++;
17353  }
17354  }
17355 
17356  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
17357 
17358  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
17359  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17360  MinuteString = " minutes";
17361  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17362  if(ArrRange == 1)
17363  {
17364  MinuteString = " minute";
17365  }
17366  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
17367  TTFile3 << ",Platforms,Trains\n\n";
17368 
17369  Ptr1 = LocServiceTimesVector.begin();
17370  Ptr2 = Ptr1 + 1;
17371  while(Ptr2 != LocServiceTimesVector.end())
17372  {
17373  PreviousService = "";
17374  NumTrainsAtLoc = 0;
17375  ServiceAndRepeatNumTotal = "";
17376  NumPlats = 0;
17377  NumPlatsAtThisLocCalculated = false;
17378  BasicTime = "";
17379  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17380  {
17381  PreviousService = "";
17382  NumTrainsAtLoc = 0;
17383  ServiceAndRepeatNumTotal = "";
17384  NumPlats = 0;
17385  NumPlatsAtThisLocCalculated = false;
17386  BasicTime = "";
17387  Ptr1++;
17388  Ptr2++;
17389  if(Ptr2 == LocServiceTimesVector.end())
17390  {
17391  break;
17392  }
17393  }
17394  if(Ptr2 == LocServiceTimesVector.end())
17395  {
17396  break;
17397  }
17398  while(Ptr2->Location == Ptr1->Location)
17399  {
17400  PreviousService = "";
17401  NumTrainsAtLoc = 0;
17402  ServiceAndRepeatNumTotal = "";
17403  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17404  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17405  {
17406  break;
17407  }
17408  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
17409  {
17410  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
17411  Ptr1++;
17412  Ptr2++;
17413  if(Ptr2 == LocServiceTimesVector.end())
17414  {
17415  break;
17416  }
17417  if(Ptr2->Location != Ptr1->Location)
17418  {
17419  break;
17420  }
17421  }
17422  if(Ptr2 == LocServiceTimesVector.end())
17423  {
17424  break;
17425  }
17426  if(Ptr2->Location != Ptr1->Location)
17427  {
17428  break;
17429  }
17430  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
17431  {
17432  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
17433  {
17434  break;
17435  }
17436  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17437  {
17438  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
17439  NumPlatsAtThisLocCalculated = true;
17440  }
17441  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17442  {
17443  if(ServiceAndRepeatNumTotal == "")
17444  {
17445  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17446  NumTrainsAtLoc = 1;
17447  }
17448  else
17449  {
17450  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17451  }
17452  }
17453  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17454  if(ServiceAndRepeatNumTotal == "")
17455  {
17456  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17457  NumTrainsAtLoc = 1;
17458  }
17459  else
17460  {
17461  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17462  }
17463  Ptr1 = Ptr2;
17464  Ptr2++;
17465  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
17466  {
17467  int MaxNumberOfSameDirections = 0;
17468  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17469  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17470  {
17471 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17472  TTFile3.close();
17473  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17474 // Utilities->CallLogPop(2224);
17475 // return false;
17476  }
17477  AnsiString Asterisk = "";
17478  if(MaxNumberOfSameDirections >= NumPlats)
17479  {
17480  Asterisk = "* ";
17481  }
17482  //print out a single line for number of trains at loc with all service refs
17483  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17484  ArrivalsPrinted = true;
17485  ServiceAndRepeatNumTotal = "";
17486  }
17487  if(Ptr2 == LocServiceTimesVector.end())
17488  {
17489  break;
17490  }
17491  if(Ptr2->Location != Ptr1->Location)
17492  {
17493  break;
17494  }
17495  }
17496  if(Ptr2 == LocServiceTimesVector.end())
17497  {
17498  break;
17499  }
17500  }
17501  }
17502  if(!ArrivalsPrinted)
17503  {
17504  TTFile3 << "Nothing to report for arrivals";
17505  }
17506  TTFile3 << "\n\n";
17507  }
17508  //end of routine for arrivals
17509  SequenceLog += "7\n";
17510  //departures
17511  if(DepChecked)
17512  {
17513  //sort in DepTime order for each location
17514  Ptr1 = LocServiceTimesVector.begin();
17515  Ptr2 = Ptr1 + 1;
17516  while(Ptr2 != LocServiceTimesVector.end())
17517  {
17518  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17519  {
17520  Ptr2++;
17521  if(Ptr2 == LocServiceTimesVector.end())
17522  {
17523  break;
17524  }
17525  }
17526  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
17527  Ptr1 = Ptr2; //first entry with next name
17528  if(Ptr2 != LocServiceTimesVector.end())
17529  {
17530  Ptr2++;
17531  }
17532  }
17533 
17534  //routine for departures - number of trains departing within the specified range with services listed at the end
17535  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
17536  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17537  MinuteString = " minutes";
17538  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17539  if(DepRange == 1)
17540  {
17541  MinuteString = " minute";
17542  }
17543  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
17544  TTFile3 << ",Platforms,Trains\n\n";
17545 
17546  Ptr1 = LocServiceTimesVector.begin();
17547  Ptr2 = Ptr1 + 1;
17548  while(Ptr2 != LocServiceTimesVector.end())
17549  {
17550  PreviousService = "";
17551  NumTrainsAtLoc = 0;
17552  ServiceAndRepeatNumTotal = "";
17553  NumPlats = 0;
17554  NumPlatsAtThisLocCalculated = false;
17555  BasicTime = "";
17556  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17557  {
17558  PreviousService = "";
17559  NumTrainsAtLoc = 0;
17560  ServiceAndRepeatNumTotal = "";
17561  NumPlats = 0;
17562  NumPlatsAtThisLocCalculated = false;
17563  BasicTime = "";
17564  Ptr1++;
17565  Ptr2++;
17566  if(Ptr2 == LocServiceTimesVector.end())
17567  {
17568  break;
17569  }
17570  }
17571  if(Ptr2 == LocServiceTimesVector.end())
17572  {
17573  break;
17574  }
17575  while(Ptr2->Location == Ptr1->Location)
17576  {
17577  PreviousService = "";
17578  NumTrainsAtLoc = 0;
17579  ServiceAndRepeatNumTotal = "";
17580  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17581  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17582  {
17583  break;
17584  }
17585  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
17586  {
17587  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
17588  Ptr1++;
17589  Ptr2++;
17590  if(Ptr2 == LocServiceTimesVector.end())
17591  {
17592  break;
17593  }
17594  if(Ptr2->Location != Ptr1->Location)
17595  {
17596  break;
17597  }
17598  }
17599  if(Ptr2 == LocServiceTimesVector.end())
17600  {
17601  break;
17602  }
17603  if(Ptr2->Location != Ptr1->Location)
17604  {
17605  break;
17606  }
17607  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
17608  {
17609  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
17610  {
17611  break;
17612  }
17613  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17614  {
17615  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
17616  NumPlatsAtThisLocCalculated = true;
17617  }
17618  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17619  {
17620  if(ServiceAndRepeatNumTotal == "")
17621  {
17622  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17623  NumTrainsAtLoc = 1;
17624  }
17625  else
17626  {
17627  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17628  }
17629  }
17630  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17631  if(ServiceAndRepeatNumTotal == "")
17632  {
17633  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17634  NumTrainsAtLoc = 1;
17635  }
17636  else
17637  {
17638  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17639  }
17640  Ptr1 = Ptr2;
17641  Ptr2++;
17642  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
17643  {
17644  int MaxNumberOfSameDirections = 0;
17645  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17646  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17647  {
17648 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17649  TTFile3.close();
17650  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17651 // Utilities->CallLogPop(2225);
17652 // return false;
17653  }
17654  AnsiString Asterisk = "";
17655  if(MaxNumberOfSameDirections >= NumPlats)
17656  {
17657  Asterisk = "* ";
17658  }
17659  //print out a single line for number of trains at loc with all service refs
17660  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17661  DeparturesPrinted = true;
17662  ServiceAndRepeatNumTotal = "";
17663  }
17664  if(Ptr2 == LocServiceTimesVector.end())
17665  {
17666  break;
17667  }
17668  if(Ptr2->Location != Ptr1->Location)
17669  {
17670  break;
17671  }
17672  }
17673  if(Ptr2 == LocServiceTimesVector.end())
17674  {
17675  break;
17676  }
17677  }
17678  }
17679  if(!DeparturesPrinted)
17680  {
17681  TTFile3 << "Nothing to report for departures";
17682  }
17683  TTFile3 << "\n\n";
17684  }
17685  //end of routine for departures
17686  SequenceLog += "8\n";
17687 
17688  //list trains at locations at same time
17689 
17690  if(AtLocChecked)
17691  {
17692  //sort in AtLocTime order for each location
17693  Ptr1 = LocServiceTimesVector.begin();
17694  Ptr2 = Ptr1 + 1;
17695  while(Ptr2 != LocServiceTimesVector.end())
17696  {
17697  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17698  {
17699  Ptr2++;
17700  if(Ptr2 == LocServiceTimesVector.end())
17701  {
17702  break;
17703  }
17704  }
17705  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
17706  Ptr1 = Ptr2; //first entry with next name
17707  if(Ptr2 != LocServiceTimesVector.end())
17708  {
17709  Ptr2++;
17710  }
17711  }
17712 
17713  //print out simultaneous AtLocs (don't need range of times for AtLocs)
17714  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
17715  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
17716  TTFile3 << ",Platforms,Trains,\n\n";
17717  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17718  Ptr1 = LocServiceTimesVector.begin();
17719  Ptr2 = Ptr1 + 1;
17720  while(Ptr2 != LocServiceTimesVector.end())
17721  {
17722  PreviousService = "";
17723  ServiceAndRepeatNumTotal = "";
17724  NumTrainsAtLoc = 0;
17725  NumPlats = 0;
17726  NumPlatsAtThisLocCalculated = false;
17727  FrhCount = 0;
17728  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17729  {
17730  PreviousService = "";
17731  ServiceAndRepeatNumTotal = "";
17732  NumTrainsAtLoc = 0;
17733  NumPlats = 0;
17734  NumPlatsAtThisLocCalculated = false;
17735  FrhCount = 0;
17736  Ptr1++;
17737  Ptr2++;
17738  if(Ptr2 == LocServiceTimesVector.end())
17739  {
17740  break;
17741  }
17742  }
17743  if(Ptr2 == LocServiceTimesVector.end())
17744  {
17745  break;
17746  }
17747  while(Ptr2->Location == Ptr1->Location)
17748  {
17749  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
17750  {
17751  FrhCount++;
17752  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17753  }
17754  PreviousService = "";
17755  NumTrainsAtLoc = 0;
17756  ServiceAndRepeatNumTotal = "";
17757  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17758  {
17759  break;
17760  }
17761  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
17762  {
17763  Ptr1++;
17764  if(Ptr1->FrhMarker == "Frh")
17765  {
17766  FrhCount++;
17767  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17768  }
17769  Ptr2++;
17770  if(Ptr2 == LocServiceTimesVector.end())
17771  {
17772  break;
17773  }
17774  if(Ptr2->Location != Ptr1->Location)
17775  {
17776  break;
17777  }
17778  }
17779  if(Ptr2 == LocServiceTimesVector.end())
17780  {
17781  break;
17782  }
17783  if(Ptr2->Location != Ptr1->Location)
17784  {
17785  break;
17786  }
17787  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
17788  {
17789  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
17790  {
17791  break;
17792  }
17793  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17794  {
17795  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
17796  NumPlatsAtThisLocCalculated = true;
17797  }
17798  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17799  {
17800  if(ServiceAndRepeatNumTotal == "")
17801  {
17802  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
17803  NumTrainsAtLoc = 1;
17804  }
17805  else
17806  {
17807  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
17808  }
17809  }
17810  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
17811  if(ServiceAndRepeatNumTotal == "")
17812  {
17813  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
17814  NumTrainsAtLoc = 1;
17815  }
17816  else
17817  {
17818  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
17819  }
17820  Ptr1 = Ptr2;
17821  if(Ptr1->FrhMarker == "Frh")
17822  {
17823  FrhCount++;
17824  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17825  }
17826  Ptr2++;
17827  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
17828  {
17829 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
17830 //new text //don't print out if all remainers or if only 1 train at loc
17831  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
17832 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
17833 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
17834  {
17835  AnsiString Asterisk = "";
17836  if(NumTrainsAtLoc > NumPlats)
17837  {
17838  Asterisk = "* ";
17839  }
17840  //print out a single line for number of trains at loc with all service refs
17841  if(FrhCount == 0)
17842  {
17843  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
17844  }
17845  else if(FrhCount == 1)
17846  {
17847  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
17848  }
17849  else
17850  {
17851  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
17852  }
17853  LastFrhCount = FrhCount;
17854  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
17855  AtLocsPrinted = true;
17856  ServiceAndRepeatNumTotal = "";
17857  }
17858  }
17859  if(Ptr2 == LocServiceTimesVector.end())
17860  {
17861  break;
17862  }
17863  if(Ptr2->Location != Ptr1->Location)
17864  {
17865  break;
17866  }
17867  }
17868  if(Ptr2 == LocServiceTimesVector.end())
17869  {
17870  break;
17871  }
17872  }
17873  }
17874  if(!AtLocsPrinted)
17875  {
17876  TTFile3 << "Nothing to report for trains at locations";
17877  }
17878  TTFile3 << "\n\n";
17879  //end of simultaneous AtLocs
17880  }
17881  SequenceLog += "9\n";
17882 /*
17883  //print out the full vector here for testing purposes
17884  TTFile3 << "Full LocServiceTimesVector\n\n";
17885  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
17886 
17887  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
17888  {
17889  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
17890  }
17891 
17892  TTFile3 << "\n\n\n";
17893 */
17894 /*cdt analysis - added at v2.10.0
17895 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
17896 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
17897 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
17898 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
17899 service.
17900 
17901 Use The TrainDataVectorCopy as that has all unique service refs.
17902 
17903 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
17904 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
17905 
17906 First create a new TrainDataVector from earlier copy as above with single services
17907 */
17908  if(DirChecked)
17909  {
17910  //direction analysis added at v2.10.0
17911  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
17912  TTrainDataVector SingleServiceVector, PartServiceVector;
17913 
17914  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
17915  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
17916  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
17917  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17918  {
17919  TTrainDataEntry TDE = TrainDataVectorCopy.at(x);
17920  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
17921  {
17922  TDE.ActionVector.erase(&TDE.ActionVector.back()); //strip repeat entry if present
17923  }
17924  const TActionVector &AV = TDE.ActionVector;
17925  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
17926  {
17927  SingleServiceEntry = TDE;
17928  TActionVector &SSAV = SingleServiceEntry.ActionVector;
17929  for(unsigned int y = 0; y < SSAV.size(); y++)
17930  {
17931  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
17932  {
17933  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
17934  break; //finished with this one
17935  }
17936  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
17937  {
17938  PartServiceEntry = TDE; //start with complete entry
17939  PartServiceEntry.ActionVector.clear(); //clear AV
17940  for(unsigned int z = 0; z <= y; z++)
17941  {
17942  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
17943  if(z == y)
17944  {
17945  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
17946  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
17947  }
17948  }
17949  PartServiceVector.push_back(PartServiceEntry);
17950  if(SSAV.at(y).Command == "fsp")
17951  {
17952  SSAV.at(y).Command = "Front split - original service continues below";
17953  SSAV.at(y).OtherHeadCode = "";
17954  }
17955  if(SSAV.at(y).Command == "rsp")
17956  {
17957  SSAV.at(y).Command = "Rear split - original service continues below";
17958  SSAV.at(y).OtherHeadCode = "";
17959  }
17960  //don't break & continue here because the original train carries on
17961  }
17962  else if(SSAV.at(y).Command == "Fns")
17963  {
17964  SSAV.at(y).Command = "chr-Fns";
17965  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17966  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17967  break; //from y loop
17968  }
17969  else if(SSAV.at(y).Command == "Fns-sh")
17970  {
17971  SSAV.at(y).Command = "chr-Fns-sh";
17972  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17973  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17974  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17975  break; //from y loop
17976  }
17977  else if(SSAV.at(y).Command == "F-nshs")
17978  {
17979  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
17980  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17981  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17982  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17983  break; //from y loop
17984  }
17985  }
17986  }
17987  }
17988  SequenceLog += "10\n";
17989  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
17990 
17991  //Now add Sns & Sns-sh services to PartServiceVector entries
17992  AnsiString NextRef;
17993  while(!PartServiceVector.empty())
17994  {
17995  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
17996  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
17997  {
17998  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
17999  {
18000  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
18001  }
18002  }
18003  //find it in TrainDataVectorCopy
18004  bool FinishType = true, FoundFlag = false;
18005  while(FinishType)
18006  {
18007  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
18008  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
18009  if(FoundFlag)
18010  {
18011  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
18012  {
18013  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
18014  {
18015  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18016  }
18017  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
18018  {
18019  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18020  }
18021  else
18022  {
18023  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
18024  {
18025  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18026  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
18027  PartServiceVector.erase(&PartServiceVector.at(0));
18028  break; //from y loop
18029  }
18030  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
18031  {
18032  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
18033  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
18034  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
18035  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
18036  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
18037  if(TempEntry.ActionVector.at(y).Command == "fsp")
18038  {
18039  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
18040  TempEntry.ActionVector.at(y).OtherHeadCode = "";
18041  }
18042  if(TempEntry.ActionVector.at(y).Command == "rsp")
18043  {
18044  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
18045  TempEntry.ActionVector.at(y).OtherHeadCode = "";
18046  }
18047  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18048  }
18049  else if(TempEntry.ActionVector.at(y).Command == "Fns")
18050  {
18051  TempEntry.ActionVector.at(y).Command = "chr-Fns";
18052  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
18053  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
18054  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
18055  break; //from y loop
18056  }
18057  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
18058  {
18059  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
18060  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18061  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
18062  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18063  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
18064  break; //from y loop
18065  }
18066  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
18067  {
18068  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
18069  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
18070  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
18071  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
18072  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
18073  break; //from y loop
18074  }
18075  }
18076  }
18077  }
18078  else
18079  {
18080  SequenceLog += + "11\n";
18081  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
18082  }
18083  }
18084  }
18085  if(!PartServiceVector.empty())
18086  {
18087  SequenceLog += "12\n";
18088  throw Exception("PartServiceVector should be empty here - size = " + PartServiceVector.size());
18089  }
18090  SequenceLog += "13\n";
18091  /*
18092  form:-
18093  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
18094  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
18095  then multiple entries, separated by commas, of the form:-
18096 
18097  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
18098  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
18099  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
18100 
18101  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
18102  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
18103  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
18104 
18105  HH:MM;Command (cdt) }TimeCmd }
18106  HH:MM;Location (arr & dep) }TimeLoc }
18107  HH:MM;HH:MM;Location }TimeTimeLoc }
18108  HH:MM;pas;Location }PassTime }
18109  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
18110  HH:MM;Fer;set of allowable IDs }ExitRailway }
18111  Command (Frh only) }FinRemHere }
18112 
18113  R;mm;dd;nn. Repeat Repeat entry
18114 
18115  Formats:
18116 
18117  Command only: Frh
18118  Time;Command: cdt
18119  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
18120  Time;Command;2 Element IDs: Snt
18121  Time;Comand;n Element IDs: Fer
18122  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
18123  Time;Command;2 Element IDs;Headcode Snt-sh
18124  Time;Command;Location pas
18125  Time;Location Arr Dep
18126  Time;Time;Location Arr & dep together
18127  */
18128 
18129 /*
18130 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
18131 checking forwards until it comes to a continuation (no report), a location name that is not null and
18132 different to the train's front element name (whether null or not) (no report), a leading point
18133 (no report) or buffers (report).
18134 */
18135  bool BufferFacingUnReportedFlag = true;
18136  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18137  {
18138  TTrackElement ThisElement, NextElement;
18139  TTrainDataEntry TDE = SingleServiceVector.at(x);
18140  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
18141  {
18142  SequenceLog += "13a\n";
18143  throw Exception("Repeat entry present in SingleServiceVector at position " + x);
18144  }
18145  const TActionVector &AV = TDE.ActionVector;
18146  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
18147  {
18148  bool BufferFlag = false;
18149  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
18150  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
18151  AnsiString FrontLocName = AV.at(0).LocationName;
18152  int NextEntryPos, NextExitPos;
18153  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
18154  int ThisExitPos;
18155  if(ThisElement.Conn[0] == RearTVPos)
18156  {
18157  ThisExitPos = 1;
18158  }
18159  else if(ThisElement.Conn[1] == RearTVPos)
18160  {
18161  ThisExitPos = 0;
18162  }
18163  else if(ThisElement.Conn[2] == RearTVPos)
18164  {
18165  ThisExitPos = 3;
18166  }
18167  else if(ThisElement.Conn[3] == RearTVPos)
18168  {
18169  ThisExitPos = 2;
18170  }
18171  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
18172  {
18173  BufferFlag = true;
18174  }
18175  else //continue tracking forwards
18176  {
18177  while(true)
18178  {
18179  if(ThisElement.Conn[ThisExitPos] == -1)
18180  {
18181  SequenceLog = "13b\n";
18182  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
18183  }
18184  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
18185  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
18186  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
18187  {
18188  BufferFlag = false; //should already be false
18189  break;
18190  }
18191  else if(NextElement.TrackType == Continuation)
18192  {
18193  BufferFlag = false;
18194  break;
18195  }
18196  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
18197  {
18198  BufferFlag = false;
18199  break;
18200  }
18201  else if(NextElement.TrackType == Buffers)
18202  {
18203  BufferFlag = true;
18204  break;
18205  }
18206  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
18207  {
18208  ThisElement = NextElement;
18209  ThisExitPos = 0;
18210  continue;
18211  }
18212  else
18213  {
18214  if(NextEntryPos == 0)
18215  {
18216  NextExitPos = 1;
18217  }
18218  else if(NextEntryPos == 1)
18219  {
18220  NextExitPos = 0;
18221  }
18222  else if(NextEntryPos == 2)
18223  {
18224  NextExitPos = 3;
18225  }
18226  else if(NextEntryPos == 3)
18227  {
18228  NextExitPos = 2;
18229  }
18230  }
18231  ThisElement = NextElement;
18232  ThisExitPos = NextExitPos;
18233  }
18234  }
18235  if(BufferFlag)
18236  {
18237  if(BufferFacingUnReportedFlag)
18238  {
18239  TTFile3 << "Train facing direction on creation analysis:-\n\n";
18240  BufferFacingUnReportedFlag = false;
18241  }
18242  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
18243  }
18244  }
18245  }
18246  if(BufferFacingUnReportedFlag)
18247  {
18248  TTFile3 << "Nothing to report for train facing directions\n\n";
18249  }
18250  else
18251  {
18252  TTFile3 << '\n';
18253  }
18254  SequenceLog += "13c\n";
18255 
18256  //Perform the missing cdt check. Check every entry simiar to the check in SecondPassActions and if find any print out the full sequence and service entries
18257  AnsiString LocationNameToBeChecked = "";
18258  bool MissingcdtUnreportedFlag = true;
18259  TNumList MarkerList;
18260  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18261  {
18262  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
18263  unsigned int y = 0;
18264  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures wont be marked if not changed
18265  bool FullBreak = false;
18266  MarkerList.clear();
18267  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
18268  // first discard unlocated Snt entries as they don't have location name set
18269  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
18270  {
18271  y = 1;
18272  }
18273  while((y < TDEntry.ActionVector.size()) && !FullBreak)
18274  // need to check each location name separately in turn, skipped for SignallerControl entries
18275  {
18276  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat) ||
18277  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
18278  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
18279  {
18280  break; // out of the 'while' loop since have reached the end
18281  }
18282  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
18283  FirstInstance = y;
18284  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
18285  {
18286  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
18287  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) ||
18288  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18289  (AVEntry.Command == "Frh-sh"))
18290  {
18291  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
18292  }
18293  if(AVEntry.Command == "cdt")
18294  {
18295  break; // out of the 'z' loop since the check is only valid up to a change of direction
18296  }
18297  if(AVEntry.LocationName == LocationNameToBeChecked)
18298  {
18299  continue; // keep going while name same
18300  }
18301  if(AVEntry.LocationName != LocationNameToBeChecked)
18302  // if name different check forwards to see if repeats
18303  {
18304  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
18305  {
18306  if(TDEntry.ActionVector.at(a).Command == "cdt")
18307  {
18308  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
18309  }
18310  if(TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked)
18311  {
18312  SecondInstance = a;
18313  AnsiString Sequence = TDEntry.ServiceReference;
18314  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18315  {
18316  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18317  {
18318  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18319  }
18320  }
18321  if(MissingcdtUnreportedFlag)
18322  {
18323  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
18324  }
18325  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
18326  MarkerList.push_back(FirstInstance);
18327  MarkerList.push_back(SecondInstance);
18328  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
18329  MissingcdtUnreportedFlag = false;
18330  FullBreak = true; //no more checks for this sequence
18331  break; //out of the a & z loops
18332  }
18333  }
18334  break; // out of the 'z' loop since have checked 'a' as far as need to
18335  }
18336  }
18337  y++;
18338  }
18339  }
18340  if(MissingcdtUnreportedFlag)
18341  {
18342  TTFile3 << "Nothing to report for missing changes of direction\n\n";
18343  }
18344  else
18345  {
18346  TTFile3 << '\n';
18347  }
18348  SequenceLog += "14\n";
18349 
18350 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
18351  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
18352  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
18353  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
18354  names on one side of a cdt already checked either by the tt validator or the missing cdt check. Then compare the two lists and if any location
18355  included in both then ok, else report as questionable. If one list is empty then it is reported.
18356 */
18357  typedef std::list<AnsiString> TLocList;
18358  TLocList BackwardList, ForwardList;
18359  bool IntroLineNeeded = true;
18360  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18361  {
18362  unsigned int cdtPosition = 9999;
18363  AnsiString cdtLocation = "";
18364  bool FoundSameName = false;
18365  MarkerList.clear();
18366  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
18367  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
18368  // need to check each location name separately in turn, skipped for SignallerControl entries
18369  {
18370  BackwardList.clear();
18371  ForwardList.clear();
18372  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
18373  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) || //end of SSVector, shouldn't be any repeats
18374  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18375  (AVEntry.Command == "Frh-sh"))
18376  {
18377  if(MarkerList.empty())
18378  {
18379  break; // out of the 'y' loop since have reached the end & nothing to report
18380  }
18381  else
18382  {
18383  AnsiString Sequence = TDEntry.ServiceReference;
18384  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18385  {
18386  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18387  {
18388  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18389  }
18390  }
18391  MarkerList.sort();
18392  if(IntroLineNeeded)
18393  {
18394  TTFile3 << "Questionable change of direction analysis.\n\n";
18395  TTFile3 << "For marked changes of direction there are no same-name locations listed both above (up to the start or another direction change)\n";
18396  TTFile3 << "and below (down to the end or another direction change) but not counting the change of direction location itself.\n\n";
18397  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
18398  TTFile3 << "make sure that none has been included incorrectly:\n\n";
18399  IntroLineNeeded = false;
18400  }
18401  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
18402  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
18403  break;
18404  }
18405  }
18406  if(AVEntry.Command != "cdt")
18407  {
18408  continue; //only looking for cdts
18409  }
18410  //here have found a cdt
18411  cdtPosition = y;
18412  cdtLocation = AVEntry.LocationName;
18413  for(int z = y - 1; z >= 0; z--)
18414  {
18415  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
18416  if(AVEntry2.Command == "cdt")
18417  {
18418  break; //don't look further back than the last cdt
18419  }
18420  if((AVEntry2.LocationName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
18421  {
18422  BackwardList.push_back(AVEntry2.LocationName);
18423  }
18424  }
18425  BackwardList.sort();
18426  BackwardList.unique();
18427  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
18428  {
18429  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
18430  if((AVEntry3.Command == "Fer") || (AVEntry3.FormatType == Repeat) ||
18431  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
18432  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
18433  {
18434  break; // out of the 'z' loop since have reached another cdt or the end
18435  }
18436  if((AVEntry3.LocationName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
18437  {
18438  ForwardList.push_back(AVEntry3.LocationName);
18439  }
18440  }
18441  ForwardList.sort();
18442  ForwardList.unique();
18443  FoundSameName = false;
18444  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
18445  if(!BackwardList.empty() && !ForwardList.empty())
18446  {
18447  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
18448  {
18449  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
18450  {
18451  if(*BLIt == *FLIt)
18452  {
18453  FoundSameName = true;
18454  }
18455  }
18456  }
18457  }
18458  if(!FoundSameName) //report the inability to find same name
18459  {
18460  MarkerList.push_back(cdtPosition);
18461  }
18462  }
18463  }
18464  if(IntroLineNeeded)
18465  {
18466  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
18467  }
18468  else
18469  {
18470  TTFile3 << '\n';
18471  }
18472 /*
18473 //print all SSVector for diagnostic purposes
18474  TTFile3 << "Whole SSVector\n\n";
18475  TNumList EmptyList;
18476  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18477  {
18478  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
18479  }
18480 */
18481  }
18482  SequenceLog += "15\n";
18483  TTFile3.close();
18484  Utilities->CallLogPop(2212);
18485  return(true);
18486  }
18487 
18488  catch(const Exception &e) //non error catch
18489  {
18490  AnsiString TTErrorFileName = "Analysis Error.txt";
18491  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
18492  std::ofstream TTError(TTErrorFileName.c_str());
18493  if(TTError == 0)
18494  {
18495  ShowMessage("Analysis error file failed to open - can't be created");
18496  Utilities->CallLogPop(2233);
18497  return(false);
18498  }
18499  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18500  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
18501  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
18502 
18503  TTError.close();
18504  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
18505  Utilities->CallLogPop(2226);
18506  return(false);
18507  }
18508 }
18509 
18510 // ---------------------------------------------------------------------------
18511 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
18512 {
18513  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
18514  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
18515  {
18516  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
18517  }
18518  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
18519  {
18520  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
18521  AnsiString Marker = "";
18522  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
18523  {
18524  Marker = ',';
18525  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
18526  {
18527  if(int(x) == *MLIt)
18528  {
18529  Marker = "-->,";
18530  break;
18531  }
18532  }
18533  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
18534  if(AVE.FormatType == StartNew)
18535  {
18536  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
18537  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
18538  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
18539  }
18540  if(AVE.FormatType == SNTShuttle)
18541  {
18542  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
18543  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
18544  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
18545  }
18546  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
18547  {
18548  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
18549  }
18550  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
18551  {
18552  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
18553  }
18554  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
18555  {
18556  TActionVectorEntry AVHolder = AVE;
18557  if(AVE.Command.SubString(1,3) == "chr")
18558  {
18559  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
18560  {
18561  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
18562  AVE.OtherHeadCode = "";
18563  }
18564  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
18565  {
18566  AVE.Command = "Change of service to ";
18567  }
18568  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
18569  {
18570  AVE.Command = "Change to shuttle finishing service";
18571  }
18572  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
18573  {
18574  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
18575  AVE.OtherHeadCode = "";
18576  }
18577  }
18578  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
18579  AVE = AVHolder;
18580  }
18581  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18582  {
18583  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
18584  }
18585  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18586  {
18587  VecFile << Marker << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
18588  }
18589  else if(AVE.FormatType == TimeTimeLoc)
18590  {
18591  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
18592  }
18593  else if(AVE.FormatType == PassTime)
18594  {
18595  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18596  }
18597  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
18598  {
18599  AnsiString ListOfExits = "";
18600  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
18601  {
18602  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
18603  }
18604  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << " Fer " << ListOfExits <<'\n';
18605  }
18606  else if(AVE.FormatType == FinRemHere)
18607  {
18608  VecFile << Marker << "Frh" << '\n';
18609  }
18610  }
18611  VecFile << '\n';
18612  }
18613  Utilities->CallLogPop(2318);
18614 }
18615 
18616 // ---------------------------------------------------------------------------
18617 
18618 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
18619 {
18620  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + ServiceReference);
18621  FoundFlag = false;
18622  FinishType = true;
18623  for(unsigned int x = 0; x < Vector.size(); x++)
18624  {
18625  if(Vector.at(x).ServiceReference == ServiceReference)
18626  {
18627  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
18628  {
18629  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
18630  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18631  {
18632  FinishType = false;
18633  }
18634  }
18635  else
18636  {
18637  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
18638  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18639  {
18640  FinishType = false;
18641  }
18642  }
18643  FoundFlag = true;
18644  Utilities->CallLogPop(2319);
18645  return(Vector.at(x));
18646  }
18647  }
18648  Utilities->CallLogPop(2320);
18649  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
18650 }
18651 
18652 // ---------------------------------------------------------------------------
18653 
18654 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
18655 {
18656 //convert times to integer minutes
18657  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
18658  if((Time1 == "") || (Time2 == ""))
18659  {
18660  Utilities->CallLogPop(2213);
18661  return(false);
18662  }
18663  int Mins = Time1.SubString(4,2).ToInt();
18664  int Hours = Time1.SubString(1,2).ToInt();
18665  int Time1Mins = (Hours * 60) + Mins;
18666  Mins = Time2.SubString(4,2).ToInt();
18667  Hours = Time2.SubString(1,2).ToInt();
18668  int Time2Mins = (Hours * 60) + Mins;
18669  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
18670  {
18671  Utilities->CallLogPop(2214);
18672  return(true);
18673  }
18674  Utilities->CallLogPop(2215);
18675  return(false);
18676 }
18677 
18678 // ---------------------------------------------------------------------------
18679 
18680 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
18681  bool &AnalysisError, int &MaxNumberOfSameDirections)
18682 {
18683  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
18684 
18685  try
18686  {
18687  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
18688  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
18689  int SCPos = 0;
18690  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
18691  //first change every second comma in Input to a semicolon so can separate services but keep times with services
18692  bool EvenComma = false;
18693  for(int x = 1; x <= Input.Length(); x++)
18694  {
18695  TempStr1 = Input[x];
18696  if(TempStr1 == AnsiString(',') && EvenComma)
18697  {
18698  TempStr2 += ';';
18699  }
18700  else
18701  {
18702  TempStr2 += Input[x];
18703  }
18704  if(TempStr1 == AnsiString(','))
18705  {
18706  EvenComma = !EvenComma;
18707  }
18708  }
18709  //load up the list of services with associated times
18710  while(TempStr2.Length() > 0)
18711  {
18712  SCPos = TempStr2.Pos(';');
18713  if(SCPos > 0) //0 if not found, as won't be when only one service left
18714  {
18715  OneService = TempStr2.SubString(1, SCPos - 1);
18716  ServiceList.push_back(OneService);
18717  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
18718  }
18719  else //no semicolon so looking at last (or only) element
18720  {
18721  ServiceList.push_back(TempStr2);
18722  TempStr2 = "";
18723  }
18724  }
18725  ServiceList.sort(); // alphabetical order
18726  ServiceList.unique(); //remove duplicates
18727  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18728 
18729  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
18730  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
18731  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
18732  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
18733 
18734  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18735  {
18736  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
18737  }
18738  SLIt3 = ServiceList.end();
18739  SLIt3--; //so points to last element
18740  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
18741  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
18742  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
18743  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
18744  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
18745  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
18746 
18747  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
18748  {
18749  SLIt = SLIt1;
18750  SLIt++; //so points to one after SLIt1
18751  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
18752  {
18753  continue; //already allocated so skip to the next
18754  }
18755  else
18756  {
18757  CommaPos1 = SLIt1->Pos(','); //can't be 0
18758  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
18759  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
18760  SpacePos = ServiceRef1.Pos(' ');
18761  RepeatNum1 = 0;
18762  if(SpacePos > 0) //otherwise it's already correct
18763  {
18764  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18765  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
18766  if(RepeatInfo1[1] == 'F')
18767  {
18768  RepeatNum1 = 0;
18769  }
18770  else
18771  {
18772  SpacePos = RepeatInfo1.Pos(' ');
18773  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
18774  }
18775  }
18776  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
18777  //but this includes the "&0" etc so need to strip these
18778  AmpersandPos = AnsiTime1.Pos('&');
18779  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
18780 
18781  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
18782  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
18783  {
18784  throw Exception("ASCLIt1 Error in " + Input);
18785  }
18786  ServiceCallingLocsList1 = ASCLIt1->second;
18787  AmpersandPos = SLIt1->Pos('&');
18788  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18789  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
18790 
18791  SameDirectionCount = 1;
18792  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
18793  {
18794  CommaPos2 = SLIt2->Pos(','); //can't be 0
18795  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
18796  //but this contains "(First service..." etc so need to strip these
18797  SpacePos = ServiceRef2.Pos(' ');
18798  RepeatNum2 = 0;
18799  if(SpacePos > 0) //otherwise it's already correct
18800  {
18801  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18802  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
18803  if(RepeatInfo2[1] == 'F')
18804  {
18805  RepeatNum2 = 0;
18806  }
18807  else
18808  {
18809  SpacePos = RepeatInfo2.Pos(' ');
18810  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
18811  }
18812  }
18813  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
18814  //but this includes the "&0" etc so need to strip these
18815  AmpersandPos = AnsiTime2.Pos('&');
18816  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
18817 
18818  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
18819  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
18820  {
18821  throw Exception("ASCLIt2 Error in " + Input);
18822  }
18823  ServiceCallingLocsList2 = ASCLIt2->second;
18824  //now compare the two
18825  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
18826  {
18827  int AmpersandPos = SLIt2->Pos('&');
18828  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18829  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
18830  SameDirectionCount++;
18831  }
18832  }
18833  if(SameDirectionCount > MaxNumberOfSameDirections)
18834  {
18835  MaxNumberOfSameDirections = SameDirectionCount;
18836  }
18837  }
18838  }
18839 
18840  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
18841  {
18842  //any existing direction so allocate it now
18843  AmpersandPos = SLIt3->Pos('&');
18844  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18845  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
18846  }
18847  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
18848  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18849  {
18850  //extract the DirectionMarker as an integer
18851  AmpersandPos = SLIt->Pos('&');
18852  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
18853  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
18854  DirectionMarker = DirectionMarkerString.ToInt();
18855  AnsiString DirectionSuffix = "";
18856  char c;
18857  if(DirectionMarker < 27)
18858  {
18859  c = 64 + DirectionMarker; //so 1 -> 'A'
18860  DirectionSuffix = "," + AnsiString(c);
18861  }
18862  else if(DirectionMarker < 53)
18863  {
18864  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
18865  DirectionSuffix = ",A" + AnsiString(c);
18866  }
18867  else
18868  {
18869  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
18870  }
18871  *SLIt = ServiceWithoutMarker + DirectionSuffix;
18872  }
18873  //now prepare the final consolidated output
18874  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18875  {
18876  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18877  }
18878  if(Output.Length() > 0)
18879  {
18880  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18881  }
18882  Utilities->CallLogPop(2216);
18883  return(Output);
18884  }
18885 
18886  catch(const Exception &e) //non error catch
18887  {
18888  AnalysisError = true;
18889  Utilities->CallLogPop(2227);
18890  return(e.Message);
18891  }
18892 }
18893 
18894 // ---------------------------------------------------------------------------
18895 
18896 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
18897 {
18898  //similar to above but doesn't include times in the input
18899  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
18900  AnsiString InternalInput = Input, Output = "", OneService = "";
18901  int CommaPos = 0;
18902  std::list<AnsiString> ServiceList;
18903  //load up the list
18904  while(InternalInput.Length() > 0)
18905  {
18906  CommaPos = InternalInput.Pos(',');
18907  if(CommaPos > 0) //0 if not found, as won't be when only one service left
18908  {
18909  OneService = InternalInput.SubString(1, CommaPos - 1);
18910  ServiceList.push_back(OneService);
18911  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
18912  }
18913  else //no comma so looking at last (or only) element
18914  {
18915  ServiceList.push_back(InternalInput);
18916  InternalInput = "";
18917  }
18918  }
18919 
18920  ServiceList.sort(); // alphabetical order
18921  ServiceList.unique(); //remove duplicates
18922  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18923  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18924  {
18925  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18926  }
18927  if(Output.Length() > 0)
18928  {
18929  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18930  }
18931  Utilities->CallLogPop(2217);
18932  return(Output);
18933 }
18934 
18935 // ---------------------------------------------------------------------------
18936 
18937 
18938 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
18939  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
18940 {
18941  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
18942  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
18943 
18944  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
18945  //general list pointers, LocPtrs point to Location in the two lists
18946 
18947  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
18948  //for List1
18949  bool LocFound = false;
18950  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
18951  int IncMinutes;
18952  TDateTime FirstServiceTime;
18953 
18954  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
18955  int Ref1Target = 0, Ref1Count = 0;
18956  int SlashPos = Ref1.Pos('/');
18957  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
18958  {
18959  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
18960  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18961  }
18962  int Ref2Target = 0, Ref2Count = 0;
18963  SlashPos = Ref2.Pos('/');
18964  if(SlashPos > 0) //if 0 leave as is
18965  {
18966  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
18967  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18968  }
18969  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
18970  {
18971  //even if others have same names. But if there are cdt's then need to refind the correct service
18972  if((*ListPtr1) == Location) //
18973  {
18974  LocPtr1 = ListPtr1; //may be modified later
18975  LocFound = true;
18976  }
18977  if(ListPtr1->SubString(1, 3) == "%%%")
18978  {
18979  AnsiString CDTTime = ListPtr1->SubString(4, 5);
18980  //now adjust the time to correspond to the repeat if there is one
18981  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
18982  {
18983  IncMinutes = -1;
18984  FirstServiceTime = TDateTime(-1);
18985  bool BreakFlag = false;
18986  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18987  {
18988  if(TDVIt->ServiceReference == Ref1)
18989  {
18990  if(Ref1Target > Ref1Count)
18991  {
18992  Ref1Count++;
18993  continue;
18994  }
18995  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
18996  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
18997  {
18998  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
18999  {
19000  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
19001  BreakFlag = true;
19002  break;
19003  }
19004  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
19005  {
19006  FirstServiceTime = AVIt->ArrivalTime;
19007  BreakFlag = true;
19008  break;
19009  }
19010  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
19011  {
19012  FirstServiceTime = AVIt->DepartureTime;
19013  BreakFlag = true;
19014  break;
19015  }
19016  }
19017  if(BreakFlag)
19018  {
19019  break;
19020  }
19021  }
19022  }
19023  if(IncMinutes == -1)
19024  {
19025  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19026  }
19027  if(FirstServiceTime == TDateTime(-1))
19028  {
19029  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19030  }
19031  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
19032  }
19033  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
19034  {
19035  LocFound = false;
19036  continue;
19037  }
19038  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
19039  {
19040  break;
19041  }
19042  if(Time1 > CDTTime) //not there yet so go on
19043  {
19044  LocFound = false;
19045  continue;
19046  }
19047  if(Time1 < CDTTime) //gone too far so can stop now
19048  {
19049  break;
19050  }
19051  }
19052  }
19053  if(!LocFound) //have to find it in both lists
19054  {
19055  Utilities->CallLogPop(2228);
19056  return( false);
19057  }
19058  //for List2
19059  LocFound = false;
19060  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
19061  {
19062  if((*ListPtr2) == Location)
19063  {
19064  LocPtr2 = ListPtr2; //may be modified later
19065  LocFound = true;
19066  }
19067  if(ListPtr2->SubString(1, 3) == "%%%")
19068  {
19069  AnsiString CDTTime = ListPtr2->SubString(4, 5);
19070  //now adjust the time to correspond to the repeat if there is one
19071  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
19072  {
19073  IncMinutes = -1;
19074  FirstServiceTime = TDateTime(-1);
19075  bool BreakFlag = false;
19076  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
19077  {
19078  if(TDVIt->ServiceReference == Ref2)
19079  {
19080  if(Ref2Target > Ref2Count)
19081  {
19082  Ref2Count++;
19083  continue;
19084  }
19085  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
19086  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
19087  {
19088  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
19089  {
19090  FirstServiceTime = AVIt->EventTime;
19091  BreakFlag = true;
19092  break;
19093  }
19094  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
19095  {
19096  FirstServiceTime = AVIt->ArrivalTime;
19097  BreakFlag = true;
19098  break;
19099  }
19100  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
19101  {
19102  FirstServiceTime = AVIt->DepartureTime;
19103  BreakFlag = true;
19104  break;
19105  }
19106  }
19107  if(BreakFlag)
19108  {
19109  break;
19110  }
19111  }
19112  }
19113  if(IncMinutes == -1)
19114  {
19115  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19116  }
19117  if(FirstServiceTime == TDateTime(-1))
19118  {
19119  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19120  }
19121  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
19122  }
19123  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
19124  {
19125  LocFound = false;
19126  continue;
19127  }
19128  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
19129  {
19130  break;
19131  }
19132  if(Time2 > CDTTime) //not there yet so go on
19133  {
19134  LocFound = false;
19135  continue;
19136  }
19137  if(Time2 < CDTTime) //gone too far so can stop now
19138  {
19139  break;
19140  }
19141  }
19142  }
19143  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
19144  {
19145  Utilities->CallLogPop(2229);
19146  return( false);
19147  }
19148  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
19149  //set ListPtr1 to the search start position
19150  if(Arrival)
19151  {
19152  LP1 = List1.begin();
19153  LP1--; //now points to before the first entry
19154  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
19155  {
19156  if(ListPtr1 == List1.begin())
19157  {
19158  break;
19159  }
19160  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
19161  {
19162  ListPtr1++; //point to one past the cdt
19163  break;
19164  }
19165  }
19166  //set ListPtr2 to the search start position
19167  LP2 = List2.begin();
19168  LP2--; //now points to before the first entry
19169  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
19170  {
19171  if(ListPtr2 == List2.begin())
19172  {
19173  break;
19174  }
19175  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
19176  {
19177  ListPtr2++; //point to one past the cdt
19178  break;
19179  }
19180  }
19181  //ListPtr1 & 2 now at search start position
19182  LP1 = ListPtr1;
19183  LP2 = ListPtr2;
19184  //now search forwards, i.e. for common locations before Location
19185  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
19186  {
19187  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
19188  {
19189  break;
19190  }
19191  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
19192  {
19193  break;
19194  }
19195  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
19196  {
19197  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
19198  {
19199  break;
19200  }
19201  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
19202  {
19203  break;
19204  }
19205  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
19206  {
19207  Utilities->CallLogPop(2230);
19208  return( true);
19209  }
19210  }
19211  }
19212  }
19213 
19214  //now, for the departure analysis, reset the start positions and search locations after Location
19215 
19216  else
19217  {
19218  LP1 = LocPtr1;
19219  LP1++; //start at one past the location itself
19220  LP2 = LocPtr2;
19221  LP2++;
19222  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
19223  {
19224  if(ListPtr1 == List1.end()) //reached end point so stop
19225  {
19226  break;
19227  }
19228  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
19229  {
19230  break;
19231  }
19232  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
19233  {
19234  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
19235  {
19236  break;
19237  }
19238  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
19239  {
19240  break;
19241  }
19242  if((*ListPtr1) == (*ListPtr2)) //found a common later location
19243  {
19244  Utilities->CallLogPop(2231);
19245  return( true);
19246  }
19247  }
19248  }
19249  }
19250  Utilities->CallLogPop(2232);
19251  return( false);
19252 }
19253 
19254 // ---------------------------------------------------------------------------
19255 
19256 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
19257 {
19258  // changed at v2.7.0 to show allowable exit elements
19259  if(ExitList.empty())
19260  {
19261  return("");
19262  }
19263  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
19264  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
19265  AnsiString ExitLocList = "";
19266  AllowedExits = "";
19267 
19268  unsigned int Counter = 0;
19269  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
19270  {
19271  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
19272  Counter++;
19273  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
19274  {
19275  ExitLocList += "\n";
19276  }
19277  }
19278  if(StartName == "")
19279  {
19280  if(ExitList.size() == 1)
19281  {
19282  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
19283  Utilities->CallLogPop(1571);
19284  return(" at " + ID);
19285  }
19286  else
19287  {
19288  Utilities->CallLogPop(1572);
19289  if(ExitList.size() < 4)
19290  {
19291  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19292  return("");
19293  }
19294  else
19295  {
19296  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19297  return("");
19298  }
19299  }
19300  }
19301  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
19302  {
19303  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
19304  {
19305  Utilities->CallLogPop(1570);
19306  if(ExitList.size() < 4)
19307  {
19308  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19309  return("");
19310  }
19311  else
19312  {
19313  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19314  return("");
19315  }
19316  }
19317  }
19318  Utilities->CallLogPop(1569);
19319  if(ExitList.size() < 4)
19320  {
19321  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19322  return(" at " + StartName);
19323  }
19324  else
19325  {
19326  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19327  return(" at " + StartName);
19328  }
19329 }
19330 
19331 // ---------------------------------------------------------------------------
19332 /* can't trust this as locations within a vector may not be contiguous
19333  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
19334  {
19335  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
19336  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
19337  //must be preceded by a TimeLoc departure
19338  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
19339  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
19340  {
19341  if((AVPtr + x) < TDEPtr->ActionVector.end())
19342  {
19343  AnsiString xx = (AVPtr + x)->Command;//test
19344  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
19345  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
19346  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
19347  {
19348  Utilities->CallLogPop();
19349  return false;
19350  }
19351  else if((AVPtr + x)->SequenceType == Finish)
19352  {
19353  Utilities->CallLogPop();
19354  return true;
19355  }
19356  }
19357  }
19358  Utilities->CallLogPop();
19359  return false;
19360  }
19361 */
19362 // ---------------------------------------------------------------------------
19363 
19364 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
19365 {
19366  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
19367  AnsiString FormatStr = "####0.0";
19368  AnsiString AvLateArrMins = "";
19369  AnsiString AvEarlyArrMins = "";
19370  AnsiString AvLatePassMins = "";
19371  AnsiString AvEarlyPassMins = "";
19372  AnsiString AvLateDepMins = "";
19373  AnsiString AvLateExitMins = "";
19374  AnsiString AvEarlyExitMins = "";
19375 
19376  //calculate remaining CumulativeDelayedRandMinsAllTrains for trains still in vector (CumulativeDelayedRandMinsAllTrains for exited or removed trains already accounted for)
19377  for(unsigned int x = 0; x < TrainVector.size(); x++)
19378  {
19379  Utilities->CumulativeDelayedRandMinsAllTrains += int(TrainVectorAt(89, x).CumulativeDelayedRandMinsOneTrain);
19380  }
19381 
19382  if(LateArrivals > 0)
19383  {
19384  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
19385  }
19386  if(EarlyArrivals > 0)
19387  {
19388  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
19389  }
19390  if(LatePasses > 0)
19391  {
19392  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
19393  }
19394  if(EarlyPasses > 0)
19395  {
19396  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
19397  }
19398  if(LateDeps > 0)
19399  {
19400  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
19401  }
19402  if(LateExits > 0) //added at v2.9.1
19403  {
19404  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
19405  }
19406  if(EarlyExits > 0) //added at v2.9.1
19407  {
19408  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
19409  }
19410  PerfFile << '\n' << '\n' << "***************************************";
19411  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
19412 
19413  if(OnTimeArrivals != 1)
19414  {
19415  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
19416  }
19417  else
19418  {
19419  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
19420  }
19421  if(LateArrivals > 1)
19422  {
19423  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
19424  }
19425  else if(LateArrivals == 1)
19426  {
19427  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
19428  }
19429  else
19430  {
19431  PerfFile << LateArrivals << " late arrivals" << '\n';
19432  }
19433  if(EarlyArrivals > 1)
19434  {
19435  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
19436  }
19437  else if(EarlyArrivals == 1)
19438  {
19439  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
19440  }
19441  else
19442  {
19443  PerfFile << EarlyArrivals << " early arrivals" << '\n';
19444  }
19445  if(OnTimePasses != 1)
19446  {
19447  PerfFile << OnTimePasses << " on-time passes" << '\n';
19448  }
19449  else
19450  {
19451  PerfFile << OnTimePasses << " on-time pass" << '\n';
19452  }
19453  if(LatePasses > 1)
19454  {
19455  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
19456  }
19457  else if(LatePasses == 1)
19458  {
19459  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
19460  }
19461  else
19462  {
19463  PerfFile << LatePasses << " late passes" << '\n';
19464  }
19465  if(EarlyPasses > 1)
19466  {
19467  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
19468  }
19469  else if(EarlyPasses == 1)
19470  {
19471  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
19472  }
19473  else
19474  {
19475  PerfFile << EarlyPasses << " early passes" << '\n';
19476  }
19477 
19478  if(OnTimeExits != 1) //this batch added at v2.9.1
19479  {
19480  PerfFile << OnTimeExits << " on-time exits" << '\n';
19481  }
19482  else
19483  {
19484  PerfFile << OnTimeExits << " on-time exit" << '\n';
19485  }
19486  if(LateExits > 1)
19487  {
19488  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
19489  }
19490  else if(LateExits == 1)
19491  {
19492  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
19493  }
19494  else
19495  {
19496  PerfFile << LateExits << " late exits" << '\n';
19497  }
19498  if(EarlyExits > 1)
19499  {
19500  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
19501  }
19502  else if(EarlyExits == 1)
19503  {
19504  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
19505  }
19506  else
19507  {
19508  PerfFile << EarlyExits << " early exits" << '\n';
19509  }
19510 
19511  if(OnTimeDeps != 1)
19512  {
19513  PerfFile << OnTimeDeps << " on-time departures" << '\n';
19514  }
19515  else
19516  {
19517  PerfFile << OnTimeDeps << " on-time departure" << '\n';
19518  }
19519  if(LateDeps > 1)
19520  {
19521  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
19522  }
19523  else if(LateDeps == 1)
19524  {
19525  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
19526  }
19527  else
19528  {
19529  PerfFile << LateDeps << " late departures" << '\n';
19530  }
19531  TDateTime TempExcessLCDownTime;
19532  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
19533  {
19534 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
19535  //later perf summaries with lower values, changed at v2.8.0
19536 // {
19537  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
19538 // }
19539 /*
19540  else
19541  {
19542  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
19543  }
19544 */
19545  if(TempExcessLCDownTime > TDateTime(0))
19546  {
19547  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
19548  }
19549  }
19550 
19551  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
19552 
19553  if(ExcessLCDownMins > 0.1)
19554  {
19555  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
19556  }
19557  if(Utilities->CumulativeDelayedRandMinsAllTrains > 0) //added at v2.13.0
19558  {
19559  PerfFile << Utilities->CumulativeDelayedRandMinsAllTrains << " minutes lost due to random delays when stopped at locations" << '\n';
19560  }
19561  if(MissedStops != 1)
19562  {
19563  PerfFile << MissedStops << " missed stops" << '\n';
19564  }
19565  else
19566  {
19567  PerfFile << MissedStops << " missed stop" << '\n';
19568  }
19569  if(OtherMissedEvents != 1)
19570  {
19571  PerfFile << OtherMissedEvents << " other missed events" << '\n';
19572  }
19573  else
19574  {
19575  PerfFile << OtherMissedEvents << " other missed event" << '\n';
19576  }
19577  if(SkippedTTEvents != 1)
19578  {
19579  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
19580  }
19581  else
19582  {
19583  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
19584  }
19585  if(UnexpectedExits != 1)
19586  {
19587  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
19588  }
19589  else
19590  {
19591  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
19592  }
19593  if(IncorrectExits != 1)
19594  {
19595  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
19596  }
19597  else
19598  {
19599  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
19600  }
19601  if(NumFailures != 1)
19602  {
19603  PerfFile << NumFailures << " train failures" << '\n';
19604  }
19605  else
19606  {
19607  PerfFile << NumFailures << " train failure" << '\n';
19608  }
19609  if(AvHoursIntValue > 0)
19610  {
19611  if(AvHoursIntValue == 1)
19612  {
19613  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
19614  }
19615  else
19616  {
19617  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
19618  }
19619  }
19620  AnsiString AvLateMinsLocsNotReached = "";
19621 
19623  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
19624  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
19625 
19626  if(LocsNotReached > 0)
19627  {
19628  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
19629  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
19630  }
19631  if(SPADRisks != 1)
19632  {
19633  PerfFile << SPADRisks << " SPAD risks" << '\n';
19634  }
19635  else
19636  {
19637  PerfFile << SPADRisks << " SPAD risk" << '\n';
19638  }
19639  if(SPADEvents != 1)
19640  {
19641  PerfFile << SPADEvents << " SPADs" << '\n';
19642  }
19643  else
19644  {
19645  PerfFile << SPADEvents << " SPAD" << '\n';
19646  }
19647  if(Derailments != 1)
19648  {
19649  PerfFile << Derailments << " derailments" << '\n';
19650  }
19651  else
19652  {
19653  PerfFile << Derailments << " derailment" << '\n';
19654  }
19655  if(CrashedTrains != 1)
19656  {
19657  PerfFile << CrashedTrains << " crashed trains" << '\n';
19658  }
19659  else
19660  {
19661  PerfFile << CrashedTrains << " crashed train" << '\n';
19662  }
19663  PerfFile << '\n' << "***************************************" << '\n';
19664 
19665  bool DerailSPADFlag = false, CrashFlag = false;
19666 
19667  int OverallScorePercent = 100;
19668  int TotArrDepExit = 0;
19669  double TotLateMinsFactor = 1;
19670  double MissedStopAndSPADRiskFactor = 1;
19671  double NetNegFactor = 1;
19672 
19674  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
19675  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
19676  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
19677  // 'no timetabled departures... message, which was inappropriate
19678 
19679  if((SPADEvents > 0) || (Derailments > 0))
19680  {
19681  OverallScorePercent = 5; // overrides other calculations
19682  DerailSPADFlag = true;
19683  }
19684  if(CrashedTrains > 0)
19685  {
19686  OverallScorePercent = 0; // overrides other calculations
19687  CrashFlag = true;
19688  }
19689  if(OverallScorePercent == 100)
19690  {
19691  int LatenessPenalty = TotLateArrMins + TotLateDepMins; //added at v2.13.0 for random delays
19692  if(Utilities->CumulativeDelayedRandMinsAllTrains > LatenessPenalty)
19693  {
19694  LatenessPenalty = 0;
19695  }
19696  else
19697  {
19698  LatenessPenalty -= Utilities->CumulativeDelayedRandMinsAllTrains;
19699  }
19700  if(TotArrDepExit > 0)
19701  {
19702  TotLateMinsFactor = exp((-0.1732) * (LatenessPenalty + OperatingTrainLateMins + NotStartedTrainLateMins + TotLateExitMins +
19703  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
19704  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
19705  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
19706  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
19707  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
19708  // of arrivals & departures, where 4% = half, 8% = a quarter etc
19709  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
19710  // NetNegfactor: product of the above two
19711  OverallScorePercent = 100 * NetNegFactor;
19712  }
19713  }
19714  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
19715  // flag condits added at v1.1.4 - see above for what the error was
19716  {
19717  AnsiString OneFailureString = ", though the failure would account for some poor performance";
19718  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
19719  AnsiString AddedString = "";
19720  if(NumFailures == 1)
19721  {
19722  AddedString = OneFailureString;
19723  }
19724  if(NumFailures > 1)
19725  {
19726  AddedString = TwoOrMoreFailureString;
19727  }
19728  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
19729  AnsiString Rating = "";
19730  if(OverallScorePercent == 100)
19731  {
19732  Rating = "Perfect!";
19733  }
19734  else if(OverallScorePercent >= 95)
19735  {
19736  Rating = "Excellent";
19737  }
19738  else if(OverallScorePercent >= 90)
19739  {
19740  Rating = "Very good";
19741  }
19742  else if(OverallScorePercent >= 80)
19743  {
19744  Rating = "Good";
19745  }
19746  else if(OverallScorePercent >= 70)
19747  {
19748  Rating = "Fair";
19749  }
19750  else if(OverallScorePercent >= 60)
19751  {
19752  Rating = "Unacceptable" + AddedString;
19753  }
19754  else if(OverallScorePercent >= 50)
19755  {
19756  Rating = "Poor" + AddedString;
19757  }
19758  else if(OverallScorePercent >= 40)
19759  {
19760  Rating = "Bad" + AddedString;
19761  }
19762  else if(OverallScorePercent >= 30)
19763  {
19764  Rating = "Very bad" + AddedString;
19765  }
19766  else if(OverallScorePercent >= 20)
19767  {
19768  Rating = "Terrible" + AddedString;
19769  }
19770  else if(OverallScorePercent >= 10)
19771  {
19772  Rating = "Appalling" + AddedString;
19773  }
19774  else if(OverallScorePercent >= 5)
19775  {
19776  if(DerailSPADFlag)
19777  {
19778  Rating = "Disastrous - potential loss of life";
19779  }
19780  // SPADs/Derailments
19781  else
19782  {
19783  Rating = "Dire" + AddedString;
19784  }
19785  }
19786  else if(OverallScorePercent < 5)
19787  {
19788  if(CrashFlag)
19789  {
19790  Rating = "Catastrophic - loss of life"; // Crashes
19791  }
19792  else
19793  {
19794  Rating = "Abysmal";
19795  }
19796  }
19797  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
19798  }
19799  else
19800  {
19801  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
19802  }
19803  PerfFile << '\n' << "***************************************";
19804  PerfFile.flush();
19805  Utilities->CallLogPop(1736);
19806 }
19807 
19808 // ---------------------------------------------------------------------------
19809 
19811 {
19812  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
19813  for(unsigned int x = 0; x < TrainVector.size(); x++)
19814  {
19815  TTrain &Train = TrainVectorAt(58, x);
19816  if(Train.Crashed)
19817  // can't use background colours for crashed & derailed because same colour
19818  {
19819  CrashWarning = true;
19820  }
19821  else if(Train.Derailed)
19822  // can't use background colours for crashed & derailed because same colour
19823  {
19824  DerailWarning = true;
19825  }
19826  else if(Train.BackgroundColour == clSPADBackground)
19827  // use colour as that changes as soon as passes signal
19828  {
19829  SPADWarning = true;
19830  }
19831  else if(Train.BackgroundColour == clTrainFailedBackground)
19832  {
19833  TrainFailedWarning = true;
19834  }
19835  else if(Train.BackgroundColour == clCallOnBackground)
19836  // use colour as also stopped at signal
19837  {
19838  CallOnWarning = true;
19839  }
19840  else if(Train.BackgroundColour == clSignalStopBackground)
19841  // use colour to distinguish from call-on
19842  {
19843  SignalStopWarning = true;
19844  }
19845  else if(Train.BackgroundColour == clBufferAttentionNeeded)
19846  // use colour to distinguish from ordinary buffer stop
19847  {
19848  BufferAttentionWarning = true;
19849  }
19850  }
19851  Utilities->CallLogPop(1796);
19852 }
19853 
19854 // ---------------------------------------------------------------------------
19855 
19857 {
19858  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
19859 
19860  // calculate lateness for running trains
19863  for(unsigned int x = 0; x < TrainVector.size(); x++)
19864  {
19865  TTrain &Train = TrainVectorAt(64, x);
19866  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
19867  AVEntryPtr++)
19868  {
19869  if(AVEntryPtr < Train.ActionVectorEntryPtr)
19870  {
19871  continue;
19872  }
19873  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.RevisedStoppedAtLoc() && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19874  TTClockTime))
19875  {
19876  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
19878  }
19879 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
19880  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19881  TTClockTime))
19882  {
19883  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
19884  OperatingTrainArrDep++;
19885  }
19886 */
19887  }
19888  }
19889 
19890  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
19893 
19894  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
19895  {
19896  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
19897  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
19898  int IncrementalMinutes = 0;
19899  if(AVEntryLast.FormatType == Repeat)
19900  {
19901  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
19902  }
19903  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
19904  {
19905  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
19906  if(TTOD.RunningEntry != NotStarted)
19907  {
19908  continue;
19909  }
19910  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
19911  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
19912  bool TrainOperatingFlag = false;
19913  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
19914  {
19915  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
19916  {
19917  TrainOperatingFlag = true;
19918  break;
19919  }
19920  }
19921  if(TrainOperatingFlag)
19922  {
19923  continue;
19924  }
19925  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
19926  {
19927  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
19928  }
19929  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
19930  {
19931  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
19932  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
19933  {
19934  break; // all the rest will also be greater (& default of -1 will be less)
19935  }
19936  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
19937  {
19938  break; // all the rest will also be greater (& default of -1 will be less)
19939  }
19940  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
19941  {
19942  break; // all the rest will also be greater (& default of -1 will be less)
19943  }
19944  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
19945  {
19946  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
19948  }
19949 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
19950  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
19951  {
19952  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
19953  NotStartedTrainArrDep++;
19954  }
19955 */
19956  }
19957  }
19958  }
19959  Utilities->CallLogPop(1894);
19960 }
19961 
19962 // ---------------------------------------------------------------------------
19963 
19965 // new v2.2.0 for OperatorActionPanel
19966 // clears entries then adds values for running trains then for continuation entries
19967 // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
19968 {
19969  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
19970  OpTimeToActMultiMap.clear();
19971  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
19972 
19973  if(!TrainVector.empty())
19974  // build OpTimeToActMultiMap entries for running trains
19975  {
19976  AnsiString HeadCode;
19977  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
19978  int TrainID;
19979  THCandTrainPosParam HCandTrainPosParam;
19980  for(unsigned int x = 0; x < TrainVector.size(); x++)
19981  {
19982  HeadCode = TrainVectorAt(62, x).HeadCode;
19983  TrainID = TrainVectorAt(63, x).TrainID;
19984  HCandTrainPosParam.first = HeadCode;
19985  HCandTrainPosParam.second = TrainID;
19986  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
19987  if((TimeToAct >= 0) && (TimeToAct < 59.9))
19988  // -1 indicates don't display
19989  {
19990  OpTimeToActMultiMapEntry.first = TimeToAct;
19991  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
19992  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
19993  }
19994  }
19995  }
19996 /*
19997  * class TContinuationTrainExpectationEntry
19998  {
19999  public:
20000  AnsiString Description; ///< service description
20001  AnsiString HeadCode; ///< service headcode
20002  int RepeatNumber; ///< service RepeatNumber
20003  int IncrementalMinutes; ///< Repeat separation in minutes
20004  int IncrementalDigits; ///< Repeat headcode separation
20005  int VectorPosition; ///< TrackVectorPosition for the continuation element
20006  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
20007  };
20008 
20009  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
20010  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
20011  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
20012  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
20013 */
20014 
20016  // build OpTimeToActMultiMap entries for expected trains
20017  {
20018  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
20019  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
20020  float TimeToAct = 0; // minutes
20021  int DistanceToRedSignal = 0; // metres
20022  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
20023  // used to ensure only one train displayed for a given continuation
20024  ContinuationEntryVecPosVector.clear();
20025  bool LaterTrain = false;
20028  {
20029  LaterTrain = false;
20030  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
20031  {
20032  CTEIt++;
20033  continue; // not interested in running or exited trains
20034  }
20035  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
20036  {
20037  CTEIt++;
20038  continue;
20039  // don't include trains not entered yet when a train is already on the continuation
20040  }
20041  if(!ContinuationEntryVecPosVector.empty())
20042  {
20043  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
20044  {
20045  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
20046  {
20047  LaterTrain = true;
20048  ;
20049  // skip past remaining trains waiting to enter at same point
20050  break;
20051  }
20052  }
20053  }
20054  if(LaterTrain)
20055  {
20056  CTEIt++;
20057  continue;
20058  }
20059  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
20060  AnsiString HeadCode = CTEIt->second.HeadCode;
20061  float CurrentStopTime; // set to 0 at start of function
20062  float LaterStopTime; // set to 0 at start of function
20063  float RecoverableTime; // set to 0 at start of function
20064  int AvTrackSpeed; // set to 0 at start of function
20065  int TrainID = -1; // not yet allocated for train still to enter
20066  int DistanceToExit; //not used for continuation entries
20067  THVShortPair ExitPair;
20068  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
20069 
20070 //at v2.11.0 found that with *AVPtr set to ...ActionVector.at(0) below instead of ...at(1) to stop signaller control trains throwing an error (because there is no ...at(1) -
20071 //discovered with Birmingham) the LaterStopTime isn't calculated and if a train does something other than depart after an arrival it is still listed in the actions due panel -
20072 //because it just calcs the distance to the red signal and converts that to a time. So here (after v2.11.0) this new test is introduced to determine whether a train is a
20073 //signaller control train (when the ActionVector size is 1) or not (ActionVector size > 1), and the ...at(value) is set accordingly - 0 for signaller control or 1 if not.
20074 
20075  int AtValue = 1;
20076  if(CTEIt->second.TrainDataEntryPtr->ActionVector.size() == 1)
20077  {
20078  AtValue = 0;
20079  }
20080  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
20081  // EntryPos always 0 for entering at a continuation
20082  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(AtValue), //see above note
20083  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
20084  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
20085  // for a train it's the one in front of LeadElement
20086  if(AvTrackSpeed < 30)
20087  {
20088  AvTrackSpeed = 30;
20089  }
20090  if(DistanceToRedSignal == -1)
20091  {
20092  TimeToAct = 60.0;
20093  }
20094  else
20095  {
20096  int Speed = AvTrackSpeed;
20097  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
20098  if(AvTrackSpeed > MaxSpeed)
20099  {
20100  Speed = MaxSpeed;
20101  }
20102  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
20103  // defined in timetable as under signaller control
20104  {
20105  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
20106  LaterStopTime = 0;
20107  }
20108  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
20109  // accel & decel taken into account in
20110  // CalcDistanceToRedSignalandStopTime
20111  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
20112  // don't need CurrentStopTime or RecoverableTime for continuation entries
20113  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
20114  TimeToAct += MinsBefEnter;
20115  }
20116  THCandTrainPosParam HCandTrainPosParam;
20117  HCandTrainPosParam.first = HeadCode;
20118  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
20119  // -1-CTE... because 2nd value covers TrainID if +ve &
20120  // continuation track vector position if -ve, -1 allows for vecpos being 0
20121  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
20122  {
20123  OpTimeToActMultiMapEntry.first = TimeToAct;
20124  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
20125  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
20126  }
20127  CTEIt++;
20128  }
20129  }
20130  Utilities->CallLogPop(2081);
20131 }
20132 
20133 // ---------------------------------------------------------------------------
20134 
20136 // new for multiplayer
20137 // clears entries then adds values for running trains
20138 {
20139  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
20140  TimeToExitMultiMap.clear();
20141  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
20142 
20143  if(!TrainVector.empty())
20144  // build map entries for running trains
20145  {
20146  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
20147  THVShortPair ExitPair;
20148  float TimeToExit;
20149  for(unsigned int x = 0; x < TrainVector.size(); x++)
20150  {
20152  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
20153  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
20154  ExitPair = TrainVectorAt(76, x).ExitPair;
20155  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
20156  {
20157  ExitInfo.TimeToExitSecs = -1;
20158  }
20159  TimeToExitMultiMapEntry.first = ExitPair;
20160  TimeToExitMultiMapEntry.second = ExitInfo;
20161  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
20162  }
20163  }
20164  Utilities->CallLogPop(2323);
20165 }
20166 
20167 // ---------------------------------------------------------------------------
20168 
20169 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
20170  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
20171  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
20172 // new v2.2.0
20173 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
20174 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
20175 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
20176 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
20177 // aren't used - this means there is no display for the train in question
20178 {
20179  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
20180  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
20181  int DistanceToRedSignal = 0;
20182  DistanceToExit = -1;
20183  ExitPair.first = -1;
20184  ExitPair.second = -1;
20185  int CumTrackSpeed = 0;
20186  // average track speed, in case need to use in time calc
20187  int TrackSpeedCount = 0;
20188  float KmPerLocationStop;
20189  float MaxAllowableSpeed;
20190 
20191  //below added at v2.6.1
20192  if(TrainID > -1)
20193  {
20194  TTrain &Train = TrainVectorAtIdent(51, TrainID);
20195  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
20196  Train.StationStopCalculated = false;
20197  }
20198  AvTrackSpeed = 0;
20199  int CurrentElement = TrackVectorPosition;
20200  int CurrentEntryPos = TrackVectorPositionEntryPos;
20201  int NextElement;
20202  int NextEntryPos;
20203  int NextExitPos;
20204 
20205  CurrentStopTime = 0;
20206  LaterStopTime = 0;
20207  RecoverableTime = 0;
20208  if(CurrentElement == -1) // end element, no action needed
20209  {
20210  Utilities->CallLogPop(2094);
20211  return(-1);
20212  }
20213  int CurrentExitPos;
20214 
20215  // get ExitPos for first element to be measured
20216  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
20217  {
20218  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
20219  {
20220  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
20221  {
20222  CurrentExitPos = 1;
20223  }
20224  else
20225  {
20226  CurrentExitPos = 3;
20227  }
20228  }
20229  else
20230  {
20231  CurrentExitPos = 0; // trailing point
20232  }
20233  }
20234  else
20235  {
20236  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
20237  }
20238  // get CumTrackSpeed for first measured element
20239 
20240  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
20241  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
20242 
20243  // check if currently stopped at a location, and if so add the remaining dwell time
20244  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
20245  if(TrainID > -1)
20246  // -1 for a continuation and can't be at a location as not yet entered
20247  {
20248  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
20250  // this used to deduct from RecoverableTime when arrive at a location
20251  if(Train.RevisedStoppedAtLoc())
20252  {
20253  if(Train.StoppedForTrainInFront)
20254  {
20255  Utilities->CallLogPop(2082);
20256  return(-1); // no action needed
20257  }
20258  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
20259  {
20260  Utilities->CallLogPop(2083);
20261  return(-1); // not due a departure so no action needed
20262  }
20263  else // due a departure
20264  {
20265  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
20266  // can't convert a TDateTime to a float directly
20267  CurrentStopTime = float(TimeToDepart);
20268  AVPtr++;
20269  }
20270  }
20271  }
20272  // check if CurrentElement is a red signal, but ok if autosig route after
20273  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
20274  // ok if autosig route after red signal
20275  {
20276  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
20277  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
20278  int RouteNumber; // holder for referenced value, not used
20279  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
20280  {
20281  Utilities->CallLogPop(2078);
20282  return(-1);
20283  }
20284  else if(SigControlAndCanPassRedSignal)
20285  // ignore signal and increment CurrentElement to NextElement
20286  {
20287  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
20288  {
20289  if((NextEntryPos == 0) || (NextEntryPos == 2))
20290  // leading entry point
20291  {
20292  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
20293  {
20294  NextExitPos = 1;
20295  }
20296  else
20297  {
20298  NextExitPos = 3;
20299  }
20300  }
20301  else
20302  {
20303  NextExitPos = 0; // trailing entry point
20304  }
20305  }
20306  else
20307  {
20308  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
20309  }
20310  CurrentElement = NextElement;
20311  CurrentEntryPos = NextEntryPos;
20312  CurrentExitPos = NextExitPos;
20313  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
20314  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
20315  }
20316  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
20317  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
20318  {
20319  Utilities->CallLogPop(2084);
20320  return(0);
20321  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
20322  }
20323  }
20324  int LaterStopNumber = 0;
20325  int x = 0;
20326  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
20327 
20328  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
20329  // not red signal next (in fwd direction) so enter loop to calc CumLength
20330  {
20331  x++; // added in v2.4.0 as above
20332  if(x > 5000)
20333  {
20334  Utilities->CallLogPop(2120);
20335  return(-1);
20336  }
20337  if(CurrentEntryPos > 1)
20338  {
20339  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
20340  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
20341  }
20342  else
20343  {
20344  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
20345  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
20346  }
20347  TrackSpeedCount++;
20348 
20349  //added for multiplayer - exiting at a continuation and continuation length already added
20350  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
20351  {
20352  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
20353  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
20354  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
20355  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
20356  //need here as next element will be -1 so will exit before calcs at end
20357  if(TrackSpeedCount > 0)
20358  {
20359  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
20360  }
20361  else // shouldn't reach here but include to prevent divide by zero error
20362  {
20363  if(CurrentEntryPos > 1)
20364  {
20365  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
20366  }
20367  else
20368  {
20369  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
20370  }
20371  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20372  }
20373  //calc AvTrackSpeed
20374  if(LaterStopNumber > 0)
20375  {
20376  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20377  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20378  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20379  // average line speed/2 (for half distance accelerating and half decelerating.
20380  }
20381  else
20382  {
20383  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20384  // using linear trendline for accel & decel distance at various speeds
20385  // at half braking, speed never < 60 using this
20386  }
20387  if(AvTrackSpeed > MaxAllowableSpeed)
20388  {
20389  AvTrackSpeed = MaxAllowableSpeed;
20390  }
20391  }
20392 
20393  // added at v2.6.1 to find DistanceToStationStop for trains running early
20394  if(TrainID > -1) //can ignore continuation entries as these don't run early
20395  {
20396  TTrain &Train = TrainVectorAtIdent(52, TrainID);
20397  if(!Train.StationStopCalculated)
20398  {
20399  if(Train.TrainMode == Timetable)
20400  {
20401  bool StopRequired = false;
20402  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
20403  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
20404  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
20405  {
20406  // no need to add in the length of element to CumulativeLength
20407  if(StopRequired)
20408  {
20409  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
20410  Train.StationStopCalculated = true; //don't want to update it with later stops
20411  }
20412  }
20413  }
20414  }
20415  }
20416  // check for train in front, but if on a bridge on other track then ok
20417  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
20418  int TrainOnElement;
20419  if(TE.TrackType != Bridge)
20420  {
20421  TrainOnElement = TE.TrainIDOnElement;
20422  }
20423  else
20424  {
20425  if(CurrentEntryPos > 1)
20426  {
20427  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
20428  }
20429  else
20430  {
20431  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
20432  }
20433  }
20434  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
20435  // train in front before red signal
20436  {
20437  Utilities->CallLogPop(2085);
20438  return(-1);
20439  }
20440  // add to stoptime if required
20441  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
20442  {
20443  double StopTimeDouble;
20444  while(AVPtr->FormatType == PassTime)
20445  {
20446  AVPtr++; // skip past any passes
20447  }
20448  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
20449  (AVPtr->FormatType == TimeTimeLoc)))
20450  // stop due here so calc dwell time & advance Ptr
20451  {
20452  if(AVPtr->FormatType == TimeTimeLoc)
20453  {
20454  LaterStopNumber++;
20455  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20456  if(StopTimeDouble < 0.5)
20457  {
20458  StopTimeDouble = 0.5;
20459  }
20460  // at least 30 secs delay at station
20461  // can't convert a TDateTime to a float directly
20462  LaterStopTime += float(StopTimeDouble);
20463  RecoverableTime += StopTimeDouble - 0.5;
20464  if((LaterStopNumber == 1) && (TrainID > -1))
20465  {
20466  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20467  }
20468  AVPtr++;
20469  }
20470  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
20471  {
20472  if((AVPtr + 1)->FormatType == TimeLoc)
20473  // must be a departure
20474  {
20475  LaterStopNumber++;
20476  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20477  // can't convert a TDateTime to a float directly
20478  if(StopTimeDouble < 0.5)
20479  {
20480  StopTimeDouble = 0.5;
20481  }
20482  // at least 30 secs delay at station
20483  LaterStopTime += float(StopTimeDouble);
20484  RecoverableTime += StopTimeDouble - 0.5;
20485  if((LaterStopNumber == 1) && (TrainID > -1))
20486  {
20487  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20488  }
20489  AVPtr++;
20490  AVPtr++;
20491  }
20492  else // not a departure, does something else at the location so no calculation needed
20493  {
20494  Utilities->CallLogPop(2086);
20495  return(-1);
20496  }
20497  }
20498  }
20499  }
20500  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
20501  if(NextElement == -1) // reached end element, no action needed
20502  {
20503  Utilities->CallLogPop(2077);
20504  return(-1);
20505  }
20506  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
20507  // get NextExitPos
20508  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
20509  {
20510  if((NextEntryPos == 0) || (NextEntryPos == 2))
20511  // leading entry point
20512  {
20513  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
20514  {
20515  NextExitPos = 1;
20516  }
20517  else
20518  {
20519  NextExitPos = 3;
20520  }
20521  }
20522  else
20523  {
20524  NextExitPos = 0; // trailing entry point
20525  }
20526  }
20527  else
20528  {
20529  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
20530  }
20531  CurrentElement = NextElement;
20532  CurrentEntryPos = NextEntryPos;
20533  CurrentExitPos = NextExitPos;
20534  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
20535  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
20536  }
20537  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
20538  // ok if autosig route after red signal, no action needed
20539  {
20540  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
20541  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
20542  int RouteNumber; // holder for referenced value, not used
20543  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
20544  {
20545  Utilities->CallLogPop(2095);
20546  return(-1);
20547  }
20548  }
20549 
20550  if(TrackSpeedCount > 0)
20551  {
20552  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
20553  }
20554  else // shouldn't reach here but include to prevent divide by zero error
20555  {
20556  if(CurrentEntryPos > 1)
20557  {
20558  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
20559  }
20560  else
20561  {
20562  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
20563  }
20564  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20565  }
20566 
20567  if(LaterStopNumber > 0)
20568  {
20569  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20570  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20571  }
20572  else
20573  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20574  // average line speed/2 (for half distance accelerating and half decelerating.
20575  {
20576  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20577  // using linear trendline for accel & decel distance at various speeds
20578  // at half braking, speed never < 60 using this
20579  }
20580  if(AvTrackSpeed > MaxAllowableSpeed)
20581  {
20582  AvTrackSpeed = MaxAllowableSpeed;
20583  }
20584  Utilities->CallLogPop(2096);
20585  return(DistanceToRedSignal);
20586 }
20587 
20588 // ---------------------------------------------------------------------------
20589 // end of TTrainController entries
20590 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8686
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17760
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:136
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:378
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:52
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:14744
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:345
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:344
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:485
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:697
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:71
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1692
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:899
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:174
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:14767
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:14412
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller, bool NoLogFlag)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6378
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:366
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:19964
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:805
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:15809
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:91
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:53
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:995
Create
@ Create
Definition: TrainUnit.h:52
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:784
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:18766
Arrive
@ Arrive
Definition: TrainUnit.h:52
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:52
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:343
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:384
Depart
@ Depart
Definition: TrainUnit.h:52
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:716
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:11598
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:714
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:426
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:780
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:281
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7235
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1609
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:53
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:905
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1621
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:711
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:142
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:764
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1633
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:66
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:66
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:502
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:745
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:67
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:430
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:71
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:19515
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13690
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:150
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:366
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:981
TTrain::TrainFailed
bool TrainFailed
Definition: TrainUnit.h:410
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:964
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:791
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:339
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:960
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:19256
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7562
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:386
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:476
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:366
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6501
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:791
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:9963
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:317
THVShortPair
std::pair< short, short > THVShortPair
Definition: InterfaceUnit.h:81
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:896
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:719
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:15540
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:110
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:850
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:987
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:994
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:185
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:18556
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:791
TTrainController::ControllerGetNewServiceDepartureInfo
AnsiString ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::GetNewServiceDepartureInfo for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:10154
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:11551
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1613
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8658
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:97
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:136
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:947
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:741
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:15555
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:265
Intermediate
@ Intermediate
Definition: TrainUnit.h:77
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:945
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6307
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:961
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3499
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:679
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:66
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:498
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:458
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:9925
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:54
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:491
NotStarted
@ NotStarted
Definition: TrainUnit.h:88
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10812
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:844
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:787
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:346
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:719
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:372
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:207
LeadMid
@ LeadMid
Definition: TrainUnit.h:298
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:834
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:19030
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:14492
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:20135
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:959
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6522
TOneRoute::RouteID
int RouteID
the ID number of the route, this is needed for session saves
Definition: TrackUnit.h:1514
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:737
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1001
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:252
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:997
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:215
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:854
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:811
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:727
TTrain::SkippedDeparture
bool SkippedDeparture
< used for terminating a service early and becoming new follow-on service
Definition: TrainUnit.h:327
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:9129
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:15658
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:298
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:222
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:891
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:15596
StartNew
@ StartNew
Definition: TrainUnit.h:66
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:52
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:298
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:784
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:347
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:53
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:446
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:713
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:994
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:18304
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:11159
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:970
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5827
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:505
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:186
TTrain
Definition: TrainUnit.h:304
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:967
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:11178
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:152
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11442
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:886
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:250
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:868
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:482
GapJump
@ GapJump
Definition: TrackUnit.h:65
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:988
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:837
NoSequence
@ NoSequence
Definition: TrainUnit.h:77
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:148
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:148
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:406
Finish
@ Finish
Definition: TrainUnit.h:77
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6258
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:9556
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:366
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1660
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:366
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:360
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:225
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:700
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:364
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:62
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:15637
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:830
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:323
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:175
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:782
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:462
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:109
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:715
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:15360
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1374
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6079
End
@ End
Definition: TrackUnit.h:75
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:396
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:7120
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8784
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:341
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10537
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:67
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:299
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:402
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:15720
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:124
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:955
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1005
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:842
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:209
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:831
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:420
SignalPost
@ SignalPost
Definition: TrackUnit.h:65
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:949
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:466
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:144
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1637
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:349
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:12239
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:977
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:209
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5586
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:150
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:845
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:768
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:895
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:418
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:74
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:478
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:64
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:840
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:805
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:893
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1878
TExitInfo
Definition: TrainUnit.h:106
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:482
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:838
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:965
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:66
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:93
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:136
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:482
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:16517
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:943
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:456
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:968
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:10496
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:739
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:366
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:454
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:81
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:993
Pass
@ Pass
Definition: TrainUnit.h:54
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:374
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:93
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:791
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:753
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:72
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3176
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11260
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:822
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:70
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:980
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:489
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:771
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:876
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:7607
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:953
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:713
TUtilities::CallLogPop
void CallLogPop(int Caller)
< specifies whether no delays or minor, moderate or major random delays are to be applied
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:354
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:317
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:986
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:817
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8670
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:989
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:211
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:66
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:221
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6972
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:808
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17480
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:470
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:15029
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10678
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:311
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3384
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1000
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:464
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:5176
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2697
RearSplit
@ RearSplit
Definition: TrainUnit.h:52
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:134
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:54
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:801
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:663
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:950
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:815
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:973
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:134
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10572
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:3108
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6944
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:15127
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:54
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:867
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:13790
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:165
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:836
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:860
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:380
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3449
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:946
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:8734
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:331
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8480
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:7759
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:3100
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:821
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:485
TTrain::GetNewServiceDepartureInfo
AnsiString GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time & next location
Definition: TrainUnit.cpp:7250
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:460
NewService
@ NewService
Definition: TrainUnit.h:52
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:15873
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1476
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:482
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:382
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1620
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:370
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:223
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:219
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:828
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TUtilities::CumulativeDelayedRandMinsAllTrains
int CumulativeDelayedRandMinsAllTrains
Definition: Utilities.h:57
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:170
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:990
Crossover
@ Crossover
Definition: TrackUnit.h:65
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4776
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:733
AtLocation
@ AtLocation
Definition: TrainUnit.h:72
Signal
@ Signal
Definition: TrackUnit.h:75
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:952
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:983
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:772
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:152
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17997
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:874
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:18896
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1621
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:462
Exited
@ Exited
Definition: TrainUnit.h:88
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10523
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:329
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:15571
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:8046
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:17823
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1002
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2314
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:482
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:59
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1642
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:942
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:482
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:751
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:313
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:793
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:52
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:428
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:805
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:972
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:948
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:366
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:66
TTrain::NewDelay
double NewDelay
< the remaining random delay at any point in time for the train
Definition: TrainUnit.h:438
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:984
Leave
@ Leave
Definition: TrainUnit.h:52
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:847
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:763
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:789
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:452
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:394
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:3081
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:951
NoFormat
@ NoFormat
Definition: TrainUnit.h:66
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:801
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:66
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2626
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:450
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:482
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3478
Enter
@ Enter
Definition: TrainUnit.h:52
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7822
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:958
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:15620
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:444
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:276
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:14320
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:743
TTrain::NewTrainService
void NewTrainService(int Caller, bool NoLogFlag)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6457
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:9164
Timetable
@ Timetable
Definition: TrainUnit.h:60
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14699
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:7029
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:799
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11111
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:764
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:132
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:17666
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:72
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:791
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:269
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:19856
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:521
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:82
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:434
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:761
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:263
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:797
Terminate
@ Terminate
Definition: TrainUnit.h:52
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:805
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:776
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:482
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:146
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:496
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:368
Running
@ Running
Definition: TrainUnit.h:88
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:285
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3575
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:894
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13662
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:15139
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:15896
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:454
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11331
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:709
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:237
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:848
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:422
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2511
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:18681
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0 for train failures, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:864
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1607
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9645
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:9217
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6871
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:858
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:975
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:127
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TUtilities::DelayMode
TDelayMode DelayMode
Definition: Utilities.h:74
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:9228
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:992
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:468
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1694
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:805
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11639
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3580
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3280
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:462
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:20169
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:784
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:72
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:54
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1605
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8627
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:3144
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:122
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:824
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:15742
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:266
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:969
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:15106
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:144
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:866
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:483
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4713
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:278
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:9979
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:53
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:356
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:404
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:11739
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:820
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:96
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:9242
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:152
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:843
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:901
TTrain::PowerAtRail
double PowerAtRail
< the running total of all random delays including knock-on delays for a single train,...
Definition: TrainUnit.h:442
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:745
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:160
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1675
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:213
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:129
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:116
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:974
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:227
TTrain::DelayedRandMins
double DelayedRandMins
Definition: TrainUnit.h:436
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:146
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:813
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:883
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:556
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5199
TTrain::TRSTime
TDateTime TRSTime
Definition: TrainUnit.h:464
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1611
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:65
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1621
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:140
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:335
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:998
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:706
Moderate
@ Moderate
Definition: Utilities.h:37
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:704
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9629
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:892
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:829
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:352
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:15909
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:748
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:963
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:150
Major
@ Major
Definition: Utilities.h:37
Points
@ Points
Definition: TrackUnit.h:65
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:11231
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:738
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:392
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:823
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:14219
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:390
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:805
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:846
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:54
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3423
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:982
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:398
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:500
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:16927
Continuation
@ Continuation
Definition: TrackUnit.h:65
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:791
TTrain::ActualArrivalTime
TDateTime ActualArrivalTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:464
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7580
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:746
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:839
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:474
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:10368
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:362
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:19364
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3238
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:897
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:54
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:774
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:900
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:996
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:10010
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:87
TTrain::FollowOnServiceRef
AnsiString FollowOnServiceRef
Definition: TrainUnit.h:325
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:480
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:673
Minor
@ Minor
Definition: Utilities.h:37
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:723
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6704
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:138
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:841
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:487
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:263
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:180
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:507
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:54
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:979
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19345
TTrain::RevisedStoppedAtLoc
bool RevisedStoppedAtLoc() const
Definition: TrainUnit.h:513
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Definition: TrainUnit.cpp:18618
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:877
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:971
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:10969
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:315
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:283
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:702
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:18511
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:715
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8590
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:65
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:791
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:320
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:760
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:187
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:721
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:966
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:18454
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18410
TTrain::CumulativeDelayedRandMinsOneTrain
double CumulativeDelayedRandMinsOneTrain
< an additional random delay at a location
Definition: TrainUnit.h:440
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:795
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:124
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:282
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:77
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:904
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:333
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:51
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:715
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:366
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:833
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1003
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:82
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9596
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:15053
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7334
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:17680
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6761
TTrain::AValue
double AValue
< only true when a train has become a follow-on service early and the follow-on service normally pass...
Definition: TrainUnit.h:414
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5639
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:18654
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9614
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:148
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13669
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:342
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:15702
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:185
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:155
NoLocation
@ NoLocation
Definition: TrainUnit.h:72
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:914
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:388
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:217
NoMode
@ NoMode
Definition: TrainUnit.h:60
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:835
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:12087
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:99
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:764
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:805
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:66
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:176
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:195
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:856
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:827
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:803
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:532
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5283
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:482
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:9998
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:962
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:140
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:297
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:376
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:776
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:108
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:240
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:14733
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7319
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:448
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:702
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1615
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:144
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:358
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:766
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:956
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:999
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:762
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3202
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled event entries, null on creation
Definition: TrainUnit.h:124
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:146
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:408
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:63
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:991
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:124
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6806
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4908
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:735
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:148
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:400
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:852
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:805
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:954
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6343
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:723
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:66
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:53
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:765
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:19810
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:731
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:66
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:819
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:985
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:870
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:717
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:128
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5188
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:432
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:148
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:721
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:337
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:734
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:727
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:67
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:832
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:3126
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:89
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:648
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1052
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:976
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:18938
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6732
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3622
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:130
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2714
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:190
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:209
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:7060
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:340
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:75
Repeat
@ Repeat
Definition: TrainUnit.h:67
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:944
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13678
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:670
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:43
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:805
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:157
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:66
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:150
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:483
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:978
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:728
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:881
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:248
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5949
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:18680
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:142
Signaller
@ Signaller
Definition: TrainUnit.h:60
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:493
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:902
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:183
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:890
TTrain::TreatPassAsTimeLocDeparture
bool TreatPassAsTimeLocDeparture
< indicates failure
Definition: TrainUnit.h:412
Bridge
@ Bridge
Definition: TrackUnit.h:65
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:957
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:280
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:366
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:76
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:424
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:77
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:65
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:134
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:124